🚧 add data binding and view model

Data are now binded on the view
features/api/requests/2
Jordan ARTZET 2 years ago
parent fc371bf386
commit e29247f3ce

@ -36,13 +36,17 @@ android {
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation "androidx.appcompat:appcompat:$rootProject.appCompatVersion"
implementation "androidx.activity:activity-ktx:$rootProject.activityVersion"
implementation 'androidx.core:core-ktx:1.9.0'
implementation "androidx.fragment:fragment-ktx:1.5.5"
// Room components
@ -53,7 +57,7 @@ dependencies {
// Lifecycle components
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-common-java8:$rootProject.lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$rootProject.lifecycleVersion"
// Kotlin components
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -69,13 +73,11 @@ dependencies {
// Moshi
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
//GSON
//implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with Scalar Converter
//implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"

@ -5,6 +5,7 @@ import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.api.dtos.PopularDTO
import fr.iut.pm.movieapplication.utils.Constants.Companion.API_KEY
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
@ -13,16 +14,16 @@ interface MovieApplicationAPI {
// Movie
@GET("movie/popular")
fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY, @Query("page") page : Int = 1) : Call<PopularDTO>
suspend fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY, @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("movie/now_playing")
fun getNowPlayingMovies(@Query("api_key") apiKey : String = API_KEY, @Query("page") page : Int = 1) : Call<PopularDTO>
suspend fun getNowPlayingMovies(@Query("api_key") apiKey : String = API_KEY, @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("movie/upcoming")
fun getUpcomingMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Call<PopularDTO>
suspend fun getUpcomingMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("movie/top_rated")
fun getTopRatedMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Call<PopularDTO>
suspend fun getTopRatedMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Response<PopularDTO>

@ -7,7 +7,7 @@ import com.squareup.moshi.JsonClass
data class MediaResultDTO(
@Json(name = "poster_path")
val posterPath : String,
val posterPath : String?,
val adult : Boolean,
val overview : String,
@Json(name = "first_air_date")
@ -25,7 +25,7 @@ data class MediaResultDTO(
val originalLanguage : String,
val title : String?,
@Json(name = "backdrop_path")
val backdropPath : String,
val backdropPath : String?,
val popularity : Double,
@Json(name = "vote_count")
val voteCount : Int,

@ -5,20 +5,17 @@ data class MediaResult(
val posterPath: String? = null,
val adult: Boolean,
val overview: String,
val firstAirDate: String? = null,
val releaseDate: String? = null,
val releaseDate: String,
val originCountry: List<String>? = null,
val genreIds: List<Int>,
val id: Int,
val originalTitle: String? = null,
val originalTitle: String,
val originalLanguage: String,
val title: String?,
val title: String,
val backdropPath: String? = null,
val popularity: Double,
val voteCount: Int,
val voteAverage: Double,
val name: String? = null,
val originalName: String? = null,
val mediaType: String?
)
{}

@ -17,6 +17,45 @@ open class Movie(
open val voteAverage: Double,
)
{
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Movie
if (posterPath != other.posterPath) return false
if (adult != other.adult) return false
if (overview != other.overview) return false
if (releaseDate != other.releaseDate) return false
if (genreIds != other.genreIds) return false
if (id != other.id) return false
if (originalTitle != other.originalTitle) return false
if (originalLanguage != other.originalLanguage) return false
if (title != other.title) return false
if (backdropPath != other.backdropPath) return false
if (popularity != other.popularity) return false
if (voteCount != other.voteCount) return false
if (video != other.video) return false
if (voteAverage != other.voteAverage) return false
return true
}
override fun hashCode(): Int {
var result = posterPath?.hashCode() ?: 0
result = 31 * result + adult.hashCode()
result = 31 * result + overview.hashCode()
result = 31 * result + releaseDate.hashCode()
result = 31 * result + genreIds.hashCode()
result = 31 * result + id
result = 31 * result + originalTitle.hashCode()
result = 31 * result + originalLanguage.hashCode()
result = 31 * result + (title?.hashCode() ?: 0)
result = 31 * result + (backdropPath?.hashCode() ?: 0)
result = 31 * result + popularity.hashCode()
result = 31 * result + voteCount
result = 31 * result + (video?.hashCode() ?: 0)
result = 31 * result + voteAverage.hashCode()
return result
}
}

@ -1,131 +1,88 @@
package fr.iut.pm.movieapplication.repository
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.liveData
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.api.dtos.PopularDTO
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.utils.MediaResultMapper
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlinx.coroutines.Dispatchers
class MovieRepository() {
class MovieRepository {
fun getPopularMovies(page : Int = 1 ,callback: (List<Movie>) -> Unit ) {
suspend fun getPopularMovies(page : Int = 1) : List<Movie>
{
val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getPopularMovies(page = page).enqueue(object :
Callback<PopularDTO> {
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
if (response.isSuccessful) {
Log.d("List :", response.body().toString())
val popularDTO = response.body()
val listMoviesDTO = popularDTO?.results
listMoviesDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!! )
}
}
callback(listMovie)
val response = RetrofitInstance.api.getPopularMovies(page = page)
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
listMediaResultDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!!)
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
})
}
else Log.d("ERROR FAILED", response.message())
return listMovie
}
fun getNowPlayingMovies(page : Int = 1, callback: (List<Movie>) -> Unit) {
suspend fun getNowPlayingMovies(page : Int = 1) : List<Movie>
{
val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getNowPlayingMovies(page = page).enqueue(object :
Callback<PopularDTO> {
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
if (response.isSuccessful) {
Log.d("List :", response.body().toString())
val popularDTO = response.body()
val listMoviesDTO = popularDTO?.results
listMoviesDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!! )
}
}
callback(listMovie)
val response = RetrofitInstance.api.getNowPlayingMovies(page = page)
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
listMediaResultDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!!)
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
})
}
else Log.d("ERROR FAILED", response.message())
return listMovie
}
fun getUpcomingMovies(page : Int = 1, callback: (List<Movie>) -> Unit) {
suspend fun getUpcomingMovies(page : Int = 1) : List<Movie>
{
val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getUpcomingMovies(page = page).enqueue(object :
Callback<PopularDTO> {
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
if (response.isSuccessful) {
Log.d("List :", response.body().toString())
val popularDTO = response.body()
val listMoviesDTO = popularDTO?.results
listMoviesDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!! )
}
}
callback(listMovie)
val response = RetrofitInstance.api.getUpcomingMovies(page = page)
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
listMediaResultDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!!)
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
})
}
else Log.d("ERROR FAILED", response.message())
return listMovie
}
fun getTopRatedMovies(page : Int = 1, callback: (List<Movie>) -> Unit) {
suspend fun getTopRatedMovies(page : Int = 1) : List<Movie>
{
val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getTopRatedMovies(page = page).enqueue(object :
Callback<PopularDTO> {
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
if (response.isSuccessful) {
Log.d("List :", response.body().toString())
val popularDTO = response.body()
val listMoviesDTO = popularDTO?.results
listMoviesDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!! )
}
}
callback(listMovie)
val response = RetrofitInstance.api.getTopRatedMovies(page = page)
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
listMediaResultDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!!)
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
})
}
else Log.d("ERROR FAILED", response.message())
return listMovie
}

@ -12,32 +12,32 @@ import retrofit2.Response
class TVShowRepository {
fun getPopularTvShow(page : Int = 1 ,callback: (List<TvShow>) -> Unit ) {
val listMovie : MutableList<TvShow> = mutableListOf()
RetrofitInstance.api.getPopularMovies(page = page).enqueue(object :
Callback<PopularDTO> {
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
if (response.isSuccessful) {
Log.d("List :", response.body().toString())
val popularDTO = response.body()
val listMoviesDTO = popularDTO?.results
listMoviesDTO?.forEach {
val tvShow = MediaResultMapper.mapToTvShow(it)
listMovie.add(tvShow)
Log.d("Movie ", tvShow.name )
}
}
callback(listMovie)
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
})
}
// fun getPopularTvShow(page : Int = 1 ,callback: (List<TvShow>) -> Unit ) {
//
// val listMovie : MutableList<TvShow> = mutableListOf()
//
// RetrofitInstance.api.getPopularMovies(page = page).enqueue(object :
// Callback<PopularDTO> {
// override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
// if (response.isSuccessful) {
// Log.d("List :", response.body().toString())
// val popularDTO = response.body()
// val listMoviesDTO = popularDTO?.results
// listMoviesDTO?.forEach {
//
// val tvShow = MediaResultMapper.mapToTvShow(it)
// listMovie.add(tvShow)
// Log.d("Movie ", tvShow.name )
// }
//
// }
// callback(listMovie)
// }
//
// override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
// Log.d("Error failure", t.message.toString())
// }
//
// })
// }
}

@ -36,7 +36,7 @@ class MainActivity : AppCompatActivity() {
}
R.id.movies_page -> {
loadFragments(MoviesFragment(this))
loadFragments(MoviesFragment())
return@setOnItemSelectedListener true
}

@ -1,70 +0,0 @@
package fr.iut.pm.movieapplication.ui.adapter
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.net.toUri
import androidx.recyclerview.widget.RecyclerView
import coil.load
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.model.media.tvshow.TvShow
import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.utils.Constants.Companion.IMG_URL
class CategoryAdapter<T>(
private val context: MainActivity,
private val layoutId: Int,
private val list: List<T>
) : RecyclerView.Adapter<MediaAdapter.ViewHolder>(){
class ViewHolder(view : View) : RecyclerView.ViewHolder(view) {
val itemImage: ImageView = view.findViewById<ImageView>(R.id.item_image)
val itemName: TextView = view.findViewById<TextView>(R.id.item_name)
val itemDate: TextView = view.findViewById<TextView>(R.id.item_date)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaAdapter.ViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(layoutId, parent, false)
return MediaAdapter.ViewHolder(view)
}
override fun getItemCount(): Int = list.size
override fun onBindViewHolder(holder: MediaAdapter.ViewHolder, position: Int) {
val currentItem = list[position]
if(currentItem != null) {
when(currentItem!!::class.java) {
Movie::class.java -> bindMovie(holder, currentItem as Movie)
TvShow::class.java -> bindTvShow(holder, currentItem as TvShow)
}
}
}
private fun bindTvShow(holder: MediaAdapter.ViewHolder, tvShow: TvShow) {
val imgUri = tvShow.posterPath?.let { (IMG_URL+it).toUri().buildUpon().scheme("https").build() }
holder.itemImage.load(imgUri)
holder.itemName.text = tvShow.name
holder.itemDate.text = tvShow.firstAirDate
}
private fun bindMovie(holder: MediaAdapter.ViewHolder, movie: Movie) {
val imgUri = movie.posterPath?.let { (IMG_URL+it).toUri().buildUpon().scheme("https").build() }
holder.itemImage.load(imgUri)
holder.itemName.text = movie.title
holder.itemDate.text = movie.releaseDate
}
private fun onItemClick(item : Movie) {
Log.d("item clicked", item.toString())
}
}

@ -37,11 +37,8 @@ class MediaAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = list[position]
when(currentItem.mediaType) {
"movie" -> bindMovie(holder, currentItem)
"tv" -> bindTvShow(holder, currentItem)
}
Log.d("SINGLETON", GlobalImageConfig.baseUrl)
bindItem(holder, currentItem)
val imgUri = currentItem.posterPath?.let {
(IMG_URL + it).toUri().buildUpon().scheme("https").build()
}
@ -55,15 +52,10 @@ class MediaAdapter(
}
// If the item is a Movie
private fun bindMovie(holder: ViewHolder, currentItem: MediaResult) {
private fun bindItem(holder: ViewHolder, currentItem: MediaResult) {
holder.itemName.text = currentItem.title
holder.itemDate.text = currentItem.releaseDate
}
// If the item is a TvShow
private fun bindTvShow(holder: ViewHolder, currentItem: MediaResult) {
holder.itemName.text = currentItem.name
holder.itemDate.text = currentItem.firstAirDate
}
private fun onItemClick(item : MediaResult) {
Log.d("item clicked", item.toString())

@ -0,0 +1,47 @@
package fr.iut.pm.movieapplication.ui.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.net.toUri
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import coil.load
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.databinding.ItemMovieCategoryBinding
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.utils.Constants
class MovieAdapter : ListAdapter<Movie, MovieAdapter.ViewHolder>(DiffUtilDogCallback) {
private object DiffUtilDogCallback : DiffUtil.ItemCallback<Movie>() {
override fun areItemsTheSame(oldItem: Movie, newItem: Movie) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Movie, newItem: Movie) = oldItem == newItem
}
class ViewHolder(private val binding : ItemMovieCategoryBinding) :
RecyclerView.ViewHolder(binding.root) {
val movie : Movie? get() = binding.movie
init {
itemView.setOnClickListener {}
}
fun bind(movie : Movie) {
binding.movie = movie
val imgUri = movie.posterPath?.let { (Constants.IMG_URL +it).toUri().buildUpon().scheme("https").build() }
binding.itemImage.load(imgUri)
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(ItemMovieCategoryBinding.inflate(LayoutInflater.from(parent.context)))
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position))
}

@ -10,7 +10,6 @@ import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.ui.adapter.MediaAdapter
import fr.iut.pm.movieapplication.ui.adapter.HomeItemDecoration
import fr.iut.pm.movieapplication.ui.adapter.CategoryAdapter
import fr.iut.pm.movieapplication.ui.viewmodel.HomeSectionsVM
class HomeSectionsFragment(
@ -30,11 +29,11 @@ class HomeSectionsFragment(
}
//get the popularity RecyclerView
context.movieRepository.getPopularMovies {
val homePopularityRecyclerView = view?.findViewById<RecyclerView>(R.id.home_popularity_recycler_view)
homePopularityRecyclerView?.adapter = CategoryAdapter(context,R.layout.item_horizontal_home_page,it)
homePopularityRecyclerView?.addItemDecoration(HomeItemDecoration())
}
// context.movieRepository.getPopularMovies {
// val homePopularityRecyclerView = view?.findViewById<RecyclerView>(R.id.home_popularity_recycler_view)
// homePopularityRecyclerView?.adapter = CategoryAdapter(context,R.layout.item_horizontal_home_page,it)
// homePopularityRecyclerView?.addItemDecoration(HomeItemDecoration())
// }
//get the free RecyclerView
val homeFreeRecyclerView = view?.findViewById<RecyclerView>(R.id.home_free_recycler_view)
homeFreeRecyclerView?.adapter = MediaAdapter(context,R.layout.item_horizontal_home_page,ArrayList())

@ -1,6 +1,7 @@
package fr.iut.pm.movieapplication.ui.fragments
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -9,19 +10,17 @@ import android.widget.ArrayAdapter
import android.widget.Spinner
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.databinding.FragmentMoviesBinding
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.ui.adapter.CategoryAdapter
import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVM
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVMFactory
import fr.iut.pm.movieapplication.utils.Constants.Companion.PAGE_SIZE
class MoviesFragment(
private val context : MainActivity
) : Fragment() {
private var isLoading = false
@ -29,150 +28,53 @@ class MoviesFragment(
private var currentPage = 1
private var currentList : MutableList<Movie> = mutableListOf()
private val moviesVM: MoviesVM by viewModels{ MoviesVMFactory(repository = MovieRepository())}
lateinit var moviesRecyclerView : RecyclerView
lateinit var spinner: Spinner
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_movies, container, false)
// Get the RecyclerView
moviesRecyclerView = view.findViewById<RecyclerView>(R.id.movies_item_recycler_view)
private val moviesVM by viewModels<MoviesVM>()
// Initialized the data inside our RecyclerView
lateinit var spinner: Spinner
// Create the ScrollListener
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = moviesRecyclerView.layoutManager as GridLayoutManager
val visibleItemCount = layoutManager.childCount
val totalItemCount = layoutManager.itemCount
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentMoviesBinding.inflate(inflater)
binding.moviesVM = moviesVM
binding.lifecycleOwner = viewLifecycleOwner
val adapter = ArrayAdapter.createFromResource(
requireContext(),
R.array.movie_filter,
android.R.layout.simple_spinner_item
)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// If we are not already loading data and it's not the last page
if(!isLoading && !isLastPage) {
if(visibleItemCount + firstVisibleItemPosition >= totalItemCount
&& firstVisibleItemPosition >= 0
&& totalItemCount >= PAGE_SIZE
with(binding.categorySpinner)
{
this.adapter = adapter
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
loadMoreMovies()
moviesVM.getDataFilter(selectedItem.toString())
}
}
}
}
// Add the ScrollLister created before to our RecyclerView
moviesRecyclerView.addOnScrollListener(scrollListener)
spinner = view.findViewById(R.id.category_spinner)
configSpinner(spinner)
return view
}
private fun configSpinner(spinner: Spinner) {
ArrayAdapter.createFromResource(
context,
R.array.movie_filter,
android.R.layout.simple_spinner_item
).also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
}
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
when(position) {
0 -> {
currentList.clear()
context.movieRepository.getPopularMovies { listMovies ->
currentList.addAll(0,listMovies)
moviesRecyclerView.adapter = CategoryAdapter(context, R.layout.item_vertical_fragment, currentList)
moviesRecyclerView.layoutManager = GridLayoutManager (context, 3)
}
}
1 -> {
currentList.clear()
context.movieRepository.getNowPlayingMovies { movies: List<Movie> ->
currentList.addAll(0,movies)
moviesRecyclerView.adapter = CategoryAdapter(context, R.layout.item_vertical_fragment, currentList)
moviesRecyclerView.layoutManager = GridLayoutManager (context, 3)
}
}
2 -> {
currentList.clear()
context.movieRepository.getUpcomingMovies { movies: List<Movie> ->
currentList.addAll(0,movies)
moviesRecyclerView.adapter = CategoryAdapter(context, R.layout.item_vertical_fragment, currentList)
moviesRecyclerView.layoutManager = GridLayoutManager (context, 3)
}
}
3 -> {
currentList.clear()
context.movieRepository.getTopRatedMovies { movies ->
currentList.addAll(0,movies)
moviesRecyclerView.adapter = CategoryAdapter(context, R.layout.item_vertical_fragment, currentList)
moviesRecyclerView.layoutManager = GridLayoutManager (context, 3)
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
return binding.root
}
/**
* Method to load data when the user reaches the bottom of the view
*/
private fun loadMoreMovies() {
isLoading = true
currentPage += 1
if(currentPage == 1000) isLastPage = true
val start = currentList.size
when(spinner.selectedItemPosition) {
0 -> {
context.movieRepository.getPopularMovies(currentPage) { listMovies ->
currentList.addAll(start, listMovies)
moviesRecyclerView.adapter?.notifyItemRangeChanged(start, listMovies.size)
}
}
1 -> {
context.movieRepository.getNowPlayingMovies(currentPage) { listMovies ->
currentList.addAll(start, listMovies)
moviesRecyclerView.adapter?.notifyItemRangeChanged(start, listMovies.size)
}
}
2 -> {
context.movieRepository.getUpcomingMovies(currentPage) { listMovies ->
currentList.addAll(start, listMovies)
moviesRecyclerView.adapter?.notifyItemRangeChanged(start, listMovies.size)
}
}
3 -> {
context.movieRepository.getTopRatedMovies(currentPage) { listMovies ->
currentList.addAll(start, listMovies)
moviesRecyclerView.adapter?.notifyItemRangeChanged(start, listMovies.size)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
moviesVM.getMoviesLiveData().observe(viewLifecycleOwner) {
moviesVM.moviesAdapter.submitList(it)
}
isLoading = false
}
}

@ -1,27 +1,93 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.*
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter
import kotlinx.coroutines.launch
class MoviesVM(private val repository: MovieRepository) : ViewModel() {
class MoviesVM() : ViewModel() {
//Movie repository
private val repository = MovieRepository()
//Live data
private var _moviesLiveData : MutableLiveData<List<Movie>> = MutableLiveData<List<Movie>>()
fun getMoviesLiveData() : LiveData<List<Movie>> = _moviesLiveData
private val _popularMovies = MutableLiveData<List<MovieDetails>>()
val popularMovies : LiveData<List<MovieDetails>> = _popularMovies
private var currentFilter = ""
val moviesAdapter = MovieAdapter()
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val layoutManager = recyclerView.layoutManager as GridLayoutManager
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
val totalItemCount = layoutManager.itemCount
init {
//loadData()
if(lastVisibleItemPosition == totalItemCount -1) {
++currentPage
getMoreData(currentPage)
}
}
}
private var currentPage = 1
fun getDataFilter(filter : String) {
//_moviesLiveData.value = mutableListOf()
currentFilter = filter
currentPage = 1
when(currentFilter) {
"Populaires" -> viewModelScope.launch {
_moviesLiveData.postValue(repository.getPopularMovies())
}
"Du moment" -> viewModelScope.launch {
_moviesLiveData.postValue(repository.getNowPlayingMovies())
}
"À venir" -> viewModelScope.launch {
_moviesLiveData.postValue(repository.getUpcomingMovies())
}
"Les mieux notés" -> viewModelScope.launch {
_moviesLiveData.postValue(repository.getTopRatedMovies())
}
}
}
fun getMoreData(page : Int = 1) {
var movies : List<Movie>
when(currentFilter) {
"Populaires" -> viewModelScope.launch {
movies = _moviesLiveData.value?.plus(repository.getPopularMovies(page)) ?: listOf()
_moviesLiveData.postValue(movies)
}
"Du moment" -> viewModelScope.launch {
movies = _moviesLiveData.value?.plus(repository.getNowPlayingMovies(page)) ?: listOf()
_moviesLiveData.postValue(movies)
}
"À venir" -> viewModelScope.launch {
movies = _moviesLiveData.value?.plus(repository.getUpcomingMovies(page)) ?: listOf()
_moviesLiveData.postValue(movies)
}
"Les mieux notés" -> viewModelScope.launch {
movies = _moviesLiveData.value?.plus(repository.getTopRatedMovies(page)) ?: listOf()
_moviesLiveData.postValue(movies)
}
}
}
}
class MoviesVMFactory(
private val repository: MovieRepository
) : ViewModelProvider.Factory {
class MoviesVMFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MoviesVM(repository) as T
return MoviesVM() as T
}
}

@ -30,7 +30,7 @@ object MediaResultMapper {
posterPath = mediaResultDTO.posterPath,
adult = !mediaResultDTO.adult,
overview = mediaResultDTO.overview,
releaseDate = mediaResultDTO.releaseDate!!,
releaseDate = mediaResultDTO.releaseDate ?: "",
genreIds = mediaResultDTO.genreIds,
id = mediaResultDTO.id,
originalTitle = mediaResultDTO.originalTitle!!,
@ -49,20 +49,17 @@ object MediaResultMapper {
posterPath = mediaResultDTO.posterPath,
adult = !mediaResultDTO.adult,
overview = mediaResultDTO.overview,
firstAirDate = mediaResultDTO.firstAirDate,
releaseDate = mediaResultDTO.releaseDate,
releaseDate = mediaResultDTO.releaseDate ?: mediaResultDTO.firstAirDate!! ,
originCountry = mediaResultDTO.originCountry,
genreIds = mediaResultDTO.genreIds,
id = mediaResultDTO.id,
originalTitle = mediaResultDTO.originalTitle,
originalTitle = mediaResultDTO.originalTitle ?: mediaResultDTO.originalName!!, //if it's not a movie also it's a tvshow
originalLanguage = mediaResultDTO.originalLanguage,
title = mediaResultDTO.title,
title = mediaResultDTO.title ?: mediaResultDTO.name!!, //if it's not a movie also it's a tvshow
backdropPath = mediaResultDTO.backdropPath,
popularity = mediaResultDTO.popularity,
voteCount = mediaResultDTO.voteCount,
voteAverage = mediaResultDTO.voteAverage,
name = mediaResultDTO.name,
originalName = mediaResultDTO.originalName,
mediaType = mediaResultDTO.mediaType
)
}

@ -1,26 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Spinner
android:id="@+id/category_spinner"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginEnd="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
android:layout_marginBottom="@dimen/default_margin"
android:background="@color/light_grey"
android:layout_marginRight="@dimen/default_margin" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/movies_item_recycler_view"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="moviesVM"
type="fr.iut.pm.movieapplication.ui.viewmodel.MoviesVM" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/default_margin"
/>
android:orientation="vertical">
<Spinner
android:id="@+id/category_spinner"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginEnd="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
android:layout_marginBottom="@dimen/default_margin"
android:background="@color/light_grey"
android:layout_marginRight="@dimen/default_margin" />
<ProgressBar
android:id="@+id/movie_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/movies_item_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/default_margin"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="3"
android:adapter="@{moviesVM.moviesAdapter}"
app:onScrollListener = "@{moviesVM.scrollListener}"
tools:listitem="@layout/item_movie_category"
/>
</LinearLayout>
</LinearLayout>
</layout>

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<data>
<variable
name="movie"
type="fr.iut.pm.movieapplication.model.media.movie.Movie" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="4dp"
android:layout_marginLeft="4dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="4dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="8dp"
app:cardCornerRadius="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/item_image"
android:layout_width="match_parent"
android:layout_height="180dp"
android:background="@color/black"
android:scaleType="centerCrop"
/>
<TextView
android:id="@+id/item_name"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center"
android:textAlignment="center"
android:text="@{movie.title}"/>
<TextView
android:id="@+id/item_date"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center"
android:textAlignment="center"
android:text="@{movie.releaseDate}"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Loading…
Cancel
Save