diff --git a/Sources/app/build.gradle b/Sources/app/build.gradle index 6940713..cd5b0a7 100644 --- a/Sources/app/build.gradle +++ b/Sources/app/build.gradle @@ -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" diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/MovieApplicationAPI.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/MovieApplicationAPI.kt index 09619f8..13efeba 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/MovieApplicationAPI.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/MovieApplicationAPI.kt @@ -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 + suspend fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY, @Query("page") page : Int = 1) : Response @GET("movie/now_playing") - fun getNowPlayingMovies(@Query("api_key") apiKey : String = API_KEY, @Query("page") page : Int = 1) : Call + suspend fun getNowPlayingMovies(@Query("api_key") apiKey : String = API_KEY, @Query("page") page : Int = 1) : Response @GET("movie/upcoming") - fun getUpcomingMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Call + suspend fun getUpcomingMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Response @GET("movie/top_rated") - fun getTopRatedMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Call + suspend fun getTopRatedMovies(@Query("api_key") apiKey: String = API_KEY, @Query("page") page : Int = 1) : Response diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/dtos/MediaResultDTO.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/dtos/MediaResultDTO.kt index 7973820..b9b56af 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/dtos/MediaResultDTO.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/api/dtos/MediaResultDTO.kt @@ -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, diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/MediaResult.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/MediaResult.kt index 73834d0..fbe2ce4 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/MediaResult.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/MediaResult.kt @@ -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? = null, val genreIds: List, 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? ) {} \ No newline at end of file diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/movie/Movie.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/movie/Movie.kt index 1a5021b..32ac156 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/movie/Movie.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/model/media/movie/Movie.kt @@ -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 + } } \ No newline at end of file diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/MovieRepository.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/MovieRepository.kt index 495a223..e327a64 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/MovieRepository.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/MovieRepository.kt @@ -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) -> Unit ) { + suspend fun getPopularMovies(page : Int = 1) : List + { val listMovie : MutableList = mutableListOf() - RetrofitInstance.api.getPopularMovies(page = page).enqueue(object : - Callback { - override fun onResponse(call: Call, response: Response) { - 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, 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) -> Unit) { + suspend fun getNowPlayingMovies(page : Int = 1) : List + { val listMovie : MutableList = mutableListOf() - RetrofitInstance.api.getNowPlayingMovies(page = page).enqueue(object : - Callback { - override fun onResponse(call: Call, response: Response) { - 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, 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) -> Unit) { + suspend fun getUpcomingMovies(page : Int = 1) : List + { val listMovie : MutableList = mutableListOf() - RetrofitInstance.api.getUpcomingMovies(page = page).enqueue(object : - Callback { - override fun onResponse(call: Call, response: Response) { - 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, 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) -> Unit) { + suspend fun getTopRatedMovies(page : Int = 1) : List + { val listMovie : MutableList = mutableListOf() - RetrofitInstance.api.getTopRatedMovies(page = page).enqueue(object : - Callback { - override fun onResponse(call: Call, response: Response) { - 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, t: Throwable) { - Log.d("Error failure", t.message.toString()) - } - - }) + } + else Log.d("ERROR FAILED", response.message()) + return listMovie } diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/TVShowRepository.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/TVShowRepository.kt index 2515701..f2c1d4a 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/TVShowRepository.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/repository/TVShowRepository.kt @@ -12,32 +12,32 @@ import retrofit2.Response class TVShowRepository { - fun getPopularTvShow(page : Int = 1 ,callback: (List) -> Unit ) { - - val listMovie : MutableList = mutableListOf() - - RetrofitInstance.api.getPopularMovies(page = page).enqueue(object : - Callback { - override fun onResponse(call: Call, response: Response) { - 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, t: Throwable) { - Log.d("Error failure", t.message.toString()) - } - - }) - } +// fun getPopularTvShow(page : Int = 1 ,callback: (List) -> Unit ) { +// +// val listMovie : MutableList = mutableListOf() +// +// RetrofitInstance.api.getPopularMovies(page = page).enqueue(object : +// Callback { +// override fun onResponse(call: Call, response: Response) { +// 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, t: Throwable) { +// Log.d("Error failure", t.message.toString()) +// } +// +// }) +// } } \ No newline at end of file diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/activity/MainActivity.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/activity/MainActivity.kt index 3648708..4e6e7db 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/activity/MainActivity.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/activity/MainActivity.kt @@ -36,7 +36,7 @@ class MainActivity : AppCompatActivity() { } R.id.movies_page -> { - loadFragments(MoviesFragment(this)) + loadFragments(MoviesFragment()) return@setOnItemSelectedListener true } diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/CategoryAdapter.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/CategoryAdapter.kt deleted file mode 100644 index 050a2ae..0000000 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/CategoryAdapter.kt +++ /dev/null @@ -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( - private val context: MainActivity, - private val layoutId: Int, - private val list: List -) : RecyclerView.Adapter(){ - - class ViewHolder(view : View) : RecyclerView.ViewHolder(view) { - val itemImage: ImageView = view.findViewById(R.id.item_image) - val itemName: TextView = view.findViewById(R.id.item_name) - val itemDate: TextView = view.findViewById(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()) - } -} \ No newline at end of file diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/MediaAdapter.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/MediaAdapter.kt index d3c6be9..bfa49cd 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/MediaAdapter.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/MediaAdapter.kt @@ -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()) diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/MovieAdapter.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/MovieAdapter.kt new file mode 100644 index 0000000..d798661 --- /dev/null +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/adapter/MovieAdapter.kt @@ -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(DiffUtilDogCallback) { + + private object DiffUtilDogCallback : DiffUtil.ItemCallback() { + 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)) +} \ No newline at end of file diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/HomeSectionsFragment.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/HomeSectionsFragment.kt index d27bc09..f58178f 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/HomeSectionsFragment.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/HomeSectionsFragment.kt @@ -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(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(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(R.id.home_free_recycler_view) homeFreeRecyclerView?.adapter = MediaAdapter(context,R.layout.item_horizontal_home_page,ArrayList()) diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/MoviesFragment.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/MoviesFragment.kt index 87fbca2..9e80f2b 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/MoviesFragment.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/fragments/MoviesFragment.kt @@ -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 = 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(R.id.movies_item_recycler_view) + private val moviesVM by viewModels() - // 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 -> - 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 -> - 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 } } diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/viewmodel/MoviesVM.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/viewmodel/MoviesVM.kt index 8548868..84bbc25 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/viewmodel/MoviesVM.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/ui/viewmodel/MoviesVM.kt @@ -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> = MutableLiveData>() + fun getMoviesLiveData() : LiveData> = _moviesLiveData - private val _popularMovies = MutableLiveData>() - val popularMovies : LiveData> = _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 + 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 create(modelClass: Class): T { - return MoviesVM(repository) as T + return MoviesVM() as T } } \ No newline at end of file diff --git a/Sources/app/src/main/java/fr/iut/pm/movieapplication/utils/MediaResultMapper.kt b/Sources/app/src/main/java/fr/iut/pm/movieapplication/utils/MediaResultMapper.kt index 9948f10..c15a018 100644 --- a/Sources/app/src/main/java/fr/iut/pm/movieapplication/utils/MediaResultMapper.kt +++ b/Sources/app/src/main/java/fr/iut/pm/movieapplication/utils/MediaResultMapper.kt @@ -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 ) } diff --git a/Sources/app/src/main/res/layout/fragment_movies.xml b/Sources/app/src/main/res/layout/fragment_movies.xml index 7a243ca..a3d0e0f 100644 --- a/Sources/app/src/main/res/layout/fragment_movies.xml +++ b/Sources/app/src/main/res/layout/fragment_movies.xml @@ -1,26 +1,56 @@ - - - - + + + + + + + + + android:orientation="vertical"> + + + + + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/Sources/app/src/main/res/layout/item_movie_category.xml b/Sources/app/src/main/res/layout/item_movie_category.xml new file mode 100644 index 0000000..784f1f9 --- /dev/null +++ b/Sources/app/src/main/res/layout/item_movie_category.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file