🔀 merge branch 'features/api/requests/2' into develop

develop
Jordan ARTZET 2 years ago
commit 10646b7c38

@ -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"

@ -1,22 +1,51 @@
package fr.iut.pm.movieapplication.api
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.api.dtos.MovieDTO
import fr.iut.pm.movieapplication.api.dtos.MovieDetailsDTO
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
interface MovieApplicationAPI {
// Movie
@GET("movie/popular")
fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY) : Call<PopularDTO>
suspend fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("movie/now_playing")
suspend fun getNowPlayingMovies(@Query("api_key") apiKey : String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("movie/upcoming")
suspend fun getUpcomingMovies(@Query("api_key") apiKey: String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("movie/top_rated")
suspend fun getTopRatedMovies(@Query("api_key") apiKey: String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
// Movie details
@GET("movie/{movie_id}")
suspend fun getMovieDetails(@Path("movie_id") movieId : Int, @Query("api_key") apiKey: String = API_KEY, @Query("language") language : String = "fr") : Response<MovieDetailsDTO>
// TvShow
@GET("tv/popular")
suspend fun getPopularTvShows(@Query("api_key") apiKey : String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("tv/airing_today")
suspend fun getAiringTodayTvShows(@Query("api_key") apiKey: String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("tv/on_the_air")
suspend fun getTvOnTheAirTvShows(@Query("api_key") apiKey: String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("tv/top_rated")
suspend fun getTopRatedTvShows(@Query("api_key") apiKey: String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<PopularDTO>
@GET("trending/{media_type}/{time_window}")
fun getTrending(@Path("media_type") mediaType : String = "all", @Path("time_window") timeWindow : String = "day", @Query("api_key") apiKey: String = API_KEY ) : Call<PopularDTO>
suspend fun getTrending(@Path("media_type") mediaType : String = "all", @Path("time_window") timeWindow : String = "day", @Query("api_key") apiKey: String = API_KEY ) : Response<PopularDTO>
@GET("movie/{movie_id")
fun getMovieDetails(@Path("movie_id") movieId : Int, @Query("api_key") apiKey: String = API_KEY) : Call<MovieResultDTO>
@GET("tv/{tv_id}")
fun getShowDetails(@Path("tv_id") tvId : Int, @Query("api_key") apiKey: String = API_KEY)
}

@ -0,0 +1,8 @@
package fr.iut.pm.movieapplication.api.dtos
data class GenreDTO(
val id : Int,
val name : String
) {
}

@ -0,0 +1,42 @@
package fr.iut.pm.movieapplication.api.dtos
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@Json(name = "results")
open class MediaResultDTO(
@Json(name = "poster_path")
val posterPath : String?,
val adult : Boolean?,
val overview : String? = null,
@Json(name = "first_air_date")
val firstAirDate : String? = null,
@Json(name = "release_date")
val releaseDate : String? = null,
@Json(name = "origin_country")
val originCountry : List<String>? = null,
// @Json(name = "genre_ids")
// val genreIds : List<Int>,
val id : Int,
@Json(name = "original_title")
val originalTitle : String? = null,
@Json(name = "original_language")
val originalLanguage : String,
val title : String? = null,
@Json(name = "backdrop_path")
val backdropPath : String?,
val popularity : Double,
@Json(name = "vote_count")
val voteCount : Int,
//val video : Boolean?,
@Json(name = "vote_average")
val voteAverage : Double,
val name: String? = null,
@Json(name = "original_name")
val originalName : String? = null,
@Json(name = "media_type")
val mediaType : String? = null
) {
}

@ -1,44 +1,29 @@
package fr.iut.pm.movieapplication.api.dtos
import androidx.room.ColumnInfo
import androidx.room.Embedded
import androidx.room.PrimaryKey
import androidx.room.Relation
import com.squareup.moshi.Json
import fr.iut.pm.movieapplication.model.Genre
import fr.iut.pm.movieapplication.model.ProductionCompany
import fr.iut.pm.movieapplication.model.ProductionCountry
import fr.iut.pm.movieapplication.network.dtos.GenreDTO
class MovieDTO (
val adult: Boolean,
val budget: Int,
val genres: Array<GenreDTO>?,
val homepage: String?,
val id: Int,
@Json(name = "original_language")
val originalLanguage: String,
@Json(name = "original_title")
val originalTitle: String,
val overview: String?,
val popularity: Double,
open class MovieDTO (
@Json(name = "poster_path")
val posterPath: String?,
@Json(name = "production_countries")
val productionCountries: Array<ProductionCountry>,
open val posterPath: String?,
open val adult: Boolean,
open val overview: String?,
@Json(name = "release_date")
val releaseDate: String,
val revenue: Int,
val runtime: Int?,
//var spokenLanguages : Array<SpokenLanguage>,
val status: String,
@Json(name = "tag_line")
val tagLine: String?,
val title: String,
@Json(name = "vote_average")
val voteAverage: Double,
open val releaseDate: String,
open val id: Int,
@Json(name = "original_title")
open val originalTitle: String,
@Json(name = "original_language")
open val originalLanguage: String,
open val title: String,
@Json(name = "backdrop_path")
open val backdropPath: String?,
open val popularity: Double,
@Json(name = "vote_count")
val voteCount: Int,
val backdropPath: String?
open val voteCount: Int,
@Json(name = "vote_average")
open val voteAverage: Double
){
}

@ -0,0 +1,37 @@
package fr.iut.pm.movieapplication.api.dtos
import com.squareup.moshi.Json
import fr.iut.pm.movieapplication.model.Genre
data class MovieDetailsDTO(
val adult: Boolean,
@Json(name = "backdrop_path")
val backdropPath: String?,
val budget: Int,
val genres: List<GenreDTO>,
val homepage: String?,
val id: Int,
@Json(name = "original_language")
val originalLanguage: String,
@Json(name = "original_title")
val originalTitle: String,
val overview: String?,
val popularity: Double,
@Json(name = "poster_path")
val posterPath: String?,
//prod companies
//prod countries
@Json(name = "release_date")
val releaseDate: String,
val revenue: Int,
//spoken language
val status: String,
val title: String,
@Json(name = "vote_average")
val voteAverage: Double,
@Json(name = "vote_count")
val voteCount: Int
)
{}

@ -28,7 +28,6 @@ class MovieResultDTO(
val popularity : Double?,
@Json(name = "vote_count")
val voteCount : Int?,
val video : Boolean?,
@Json(name = "vote_average")
val voteAverage : Double?

@ -9,7 +9,7 @@ class PopularDTO(
@Json(name = "page")
val page : Int,
@Json(name = "results")
val results : List<MovieResultDTO>,
val results : List<MediaResultDTO>,
@Json(name = "total_results")
val totalResults : Int,
@Json(name = "total_pages")

@ -0,0 +1,30 @@
package fr.iut.pm.movieapplication.api.dtos
import com.squareup.moshi.Json
open class TvShowDetailsDTO(
@Json(name = "poster_path")
open val posterPath: String?,
open val popularity: Double,
open val id: Int,
@Json(name = "backdrop_path")
open val backdropPath: String?,
@Json(name = "vote_average")
open val voteAverage: Double,
open val overview: String,
@Json(name = "first_air_date")
open val firstAirDate: String,
@Json(name = "origin_country")
open val originCountry: List<String>,
@Json(name = "genre_ids")
open val genreIds: List<Int>,
@Json(name = "original_language")
open val originalLanguage: String,
@Json(name = "vote_count")
open val voteCount: Int,
open val name: String,
@Json(name = "original_name")
open val originalName: String,
)
{}

@ -4,17 +4,17 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import kotlinx.coroutines.flow.Flow
@Dao
interface MovieDAO {
@Query("SELECT * FROM movies_table ORDER BY original_title ASC")
fun getMovieByAlphabetizeMovie() : Flow<List<Movie>>
fun getMovieByAlphabetizeMovie() : Flow<List<MovieDetails>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(movie : Movie)
suspend fun insert(movie : MovieDetails)
@Query("DELETE FROM movies_table")
suspend fun deleteAll()

@ -1,16 +1,7 @@
package fr.iut.pm.movieapplication.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "genre_table")
data class Genre(
@PrimaryKey
@ColumnInfo(name = "id")
var id : Int,
@ColumnInfo(name = "name")
var name : String
val id : Int,
val name : String
) {
override fun equals(other: Any?): Boolean {

@ -1,64 +0,0 @@
package fr.iut.pm.movieapplication.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "movies_table")
data class Movie(
@ColumnInfo(name = "adult")
val adult: Boolean,
@ColumnInfo(name = "budget")
val budget: Int?,
@ColumnInfo(name = "genres")
val genres: Array<Genre>?,
@ColumnInfo(name = "homepage")
val homePage: String?,
@PrimaryKey
@ColumnInfo(name = "id")
val movieId: Int,
@ColumnInfo(name = "original_language")
val originalLanguage: String?,
@ColumnInfo(name = "original_title")
val originalTitle: String?,
val overview: String?,
val popularity: Double?,
@ColumnInfo(name = "poster_path")
val posterPath: String?,
val productionCompanies: Array<ProductionCompany>?,
@ColumnInfo(name = "production_countries")
val productionCountries: Array<ProductionCountry>?,
@ColumnInfo(name = "release_date")
val releaseDate: String?,
val revenue: Int?,
val runtime: Int?,
//var spokenLanguages : Array<SpokenLanguage>,
val status: String?,
@ColumnInfo(name = "tag_line")
val tagLine: String?,
val title: String?,
@ColumnInfo(name = "vote_average")
val voteAverage: Double?,
@ColumnInfo(name = "vote_count")
val voteCount: Int?,
val backdropPath: String?
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Movie
if (movieId != other.movieId) return false
return true
}
override fun hashCode(): Int {
return movieId
}
}

@ -2,13 +2,14 @@ package fr.iut.pm.movieapplication.model
import androidx.room.ColumnInfo
import androidx.room.Embedded
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
data class Popular(
@ColumnInfo("page")
val page : Int,
@ColumnInfo("results")
@Embedded
val results : List<Movie>,
val results : List<MovieDetails>,
val totalResults : Int,
val totalPages : Int

@ -0,0 +1,21 @@
package fr.iut.pm.movieapplication.model.media
data class MediaResult(
val posterPath: String? = null,
val adult: Boolean?,
val overview: String,
val releaseDate: String,
val originCountry: List<String>? = null,
// val genreIds: List<Int>,
val id: Int,
val originalTitle: String,
val originalLanguage: String,
val title: String,
val backdropPath: String? = null,
val popularity: Double,
val voteCount: Int,
val voteAverage: Double,
val mediaType: String?
)
{}

@ -0,0 +1,55 @@
package fr.iut.pm.movieapplication.model.media.movie
open class Movie(
open val posterPath: String?,
open val adult: Boolean?,
open val overview: String?,
open val releaseDate: String?,
open val id: Int,
open val originalTitle: String,
open val originalLanguage: String,
open val title: String,
open val backdropPath: String?,
open val popularity: Double,
open val voteCount: Int,
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 (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 (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 + 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 + voteAverage.hashCode()
return result
}
}

@ -0,0 +1,34 @@
package fr.iut.pm.movieapplication.model.media.movie
import fr.iut.pm.movieapplication.model.Genre
class MovieDetails(
adult : Boolean?,
backdropPath : String?,
val budget : Int,
val genres : List<Genre>,
val homepage : String?,
id : Int,
originalLanguage : String,
originalTitle : String,
overview : String?,
popularity : Double,
posterPath : String?,
//prod companies
//prod countries
releaseDate : String?,
val revenue : Int,
//spoken language
val status : String,
title : String,
voteAverage : Double,
voteCount : Int
) : Movie(posterPath, adult, overview, releaseDate, id, originalTitle, originalLanguage,
title, backdropPath, popularity, voteCount, voteAverage)
{
}

@ -0,0 +1,62 @@
package fr.iut.pm.movieapplication.model.media.tvshow
open class TvShow(
open val posterPath: String?,
open val popularity: Double,
open val id: Int,
open val backdropPath: String,
open val voteAverage: Double,
open val overview: String,
open val firstAirDate: String?,
open val originCountry: List<String>,
// open val genreIds: List<Int>,
open val originalLanguage: String,
open val voteCount: Int,
open val name: String,
open val originalName: String,
open val mediaType: String = "tv",
)
{
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TvShow
if (posterPath != other.posterPath) return false
if (popularity != other.popularity) return false
if (id != other.id) return false
if (backdropPath != other.backdropPath) return false
if (voteAverage != other.voteAverage) return false
if (overview != other.overview) return false
if (firstAirDate != other.firstAirDate) return false
if (originCountry != other.originCountry) return false
// if (genreIds != other.genreIds) return false
if (originalLanguage != other.originalLanguage) return false
if (voteCount != other.voteCount) return false
if (name != other.name) return false
if (originalName != other.originalName) return false
if (mediaType != other.mediaType) return false
return true
}
override fun hashCode(): Int {
var result = posterPath?.hashCode() ?: 0
result = 31 * result + popularity.hashCode()
result = 31 * result + id
result = 31 * result + backdropPath.hashCode()
result = 31 * result + voteAverage.hashCode()
result = 31 * result + overview.hashCode()
result = 31 * result + (firstAirDate?.hashCode() ?: 0)
result = 31 * result + originCountry.hashCode()
// result = 31 * result + genreIds.hashCode()
result = 31 * result + originalLanguage.hashCode()
result = 31 * result + voteCount
result = 31 * result + name.hashCode()
result = 31 * result + originalName.hashCode()
result = 31 * result + mediaType.hashCode()
return result
}
}

@ -0,0 +1,4 @@
package fr.iut.pm.movieapplication.model.media.tvshow
class TvShowDetails {
}

@ -1,7 +0,0 @@
package fr.iut.pm.movieapplication.network.dtos
data class GenreDTO(
private val id : Int,
private val name : String
) {
}

@ -0,0 +1,71 @@
package fr.iut.pm.movieapplication.repository
import android.util.Log
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.utils.MediaResultMapper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class MediaRepository {
suspend fun getTrends() : List<MediaResult> = withContext(Dispatchers.IO) {
val listMediaResult : MutableList<MediaResult> = mutableListOf()
val response = RetrofitInstance.api.getTrending()
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
Log.d("Response",response.body().toString())
listMediaResultDTO?.forEach {
val mediaResult = MediaResultMapper.mapToMediaResult(it)
listMediaResult.add(mediaResult)
mediaResult.title?.let { it1 -> Log.d("Movie", it1) }
}
}
else Log.d("ERROR FAILED", response.message())
listMediaResult
}
suspend fun getPopularMovies() : List<MediaResult> = withContext(Dispatchers.IO) {
val listMediaResult : MutableList<MediaResult> = mutableListOf()
val response = RetrofitInstance.api.getPopularMovies()
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
Log.d("Response",response.body().toString())
listMediaResultDTO?.forEach {
val mediaResult = MediaResultMapper.mapToMediaResult(it)
listMediaResult.add(mediaResult)
mediaResult.title?.let { it1 -> Log.d("Movie", it1) }
}
}
else Log.d("ERROR FAILED", response.message())
listMediaResult
}
suspend fun getPopularTvShows(): List<MediaResult> = withContext(Dispatchers.IO) {
val listMediaResult : MutableList<MediaResult> = mutableListOf()
val response = RetrofitInstance.api.getPopularTvShows()
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
Log.d("Response",response.body().toString())
listMediaResultDTO?.forEach {
val mediaResult = MediaResultMapper.mapToMediaResult(it)
listMediaResult.add(mediaResult)
mediaResult.title?.let { it1 -> Log.d("Movie", it1) }
}
}
else Log.d("ERROR FAILED", response.message())
listMediaResult
}
}

@ -2,65 +2,104 @@ package fr.iut.pm.movieapplication.repository
import android.util.Log
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.api.dtos.PopularDTO
import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.utils.Constants.Companion.API_KEY
import fr.iut.pm.movieapplication.utils.Mapper
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import fr.iut.pm.movieapplication.api.dtos.MovieDetailsDTO
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import fr.iut.pm.movieapplication.utils.MediaResultMapper
import fr.iut.pm.movieapplication.utils.MovieMapper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class MovieRepository() {
fun getPopularMovies(callback: (List<Movie>) -> Unit ) {
class MovieRepository {
suspend fun getPopularMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getPopularMovies().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 = Mapper.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!! + " " + movie.id)
}
}
else Log.d("ERROR FAILED", response.message())
listMovie
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
suspend fun getNowPlayingMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
})
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!!)
}
}
else Log.d("ERROR FAILED", response.message())
listMovie
}
fun getTrends(callback: (List<Movie>) -> Unit) {
suspend fun getUpcomingMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getTrending().enqueue(object : Callback<PopularDTO> {
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
if(response.isSuccessful) {
Log.d("Response",response.body().toString())
val popularDTO = response.body()
popularDTO?.results?.forEach {
val movie = Mapper.MapToMovie(it)
listMovie.add(movie)
movie.title?.let { it1 -> Log.d("Movie", it1) }
}
}
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!!)
}
}
else Log.d("ERROR FAILED", response.message())
listMovie
}
suspend fun getTopRatedMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
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!!)
}
})
}
else Log.d("ERROR FAILED", response.message())
listMovie
}
suspend fun getMovieDetails(id : Int) : MovieDetails?
{
var movieDetails : MovieDetails? = null
val response = RetrofitInstance.api.getMovieDetails(id)
if(response.isSuccessful && response.body() != null) {
Log.d("SUCCESS", response.body().toString())
movieDetails = MovieMapper.mapToMovieDetails(response.body()!!)
Log.d("Movie details",movieDetails.toString())
}
else Log.d("ERROR FAILED", response.toString())
return movieDetails
}
}

@ -0,0 +1,89 @@
package fr.iut.pm.movieapplication.repository
import android.util.Log
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.model.media.tvshow.TvShow
import fr.iut.pm.movieapplication.utils.MediaResultMapper
class TvShowRepository {
suspend fun getPopularTvShows(page : Int = 1 ) : List<TvShow> {
val listMovie : MutableList<TvShow> = mutableListOf()
val response = RetrofitInstance.api.getPopularTvShows(page = page)
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 )
}
}
else Log.d("Error failure", response.message())
return listMovie
}
suspend fun getAiringTodayTvShows(page : Int = 1 ) : List<TvShow> {
val listMovie : MutableList<TvShow> = mutableListOf()
val response = RetrofitInstance.api.getAiringTodayTvShows(page = page)
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 )
}
}
else Log.d("Error failure", response.message())
return listMovie
}
suspend fun getTvOnTheAirTvShows(page : Int = 1 ) : List<TvShow> {
val listMovie : MutableList<TvShow> = mutableListOf()
val response = RetrofitInstance.api.getTvOnTheAirTvShows(page = page)
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 )
}
}
else Log.d("Error failure", response.message())
return listMovie
}
suspend fun getTopRatedTvShows(page : Int = 1 ) : List<TvShow> {
val listMovie : MutableList<TvShow> = mutableListOf()
val response = RetrofitInstance.api.getTopRatedTvShows(page = page)
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 )
}
}
else Log.d("Error failure", response.message())
return listMovie
}
}

@ -5,27 +5,18 @@ import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.View
import android.widget.SearchView.OnQueryTextListener
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import com.google.android.material.bottomnavigation.BottomNavigationView
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.api.config.GlobalImageConfig
import fr.iut.pm.movieapplication.repository.MediaRepository
import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.ui.fragments.HomeSectionsFragment
import fr.iut.pm.movieapplication.ui.fragments.MoviesFragment
import fr.iut.pm.movieapplication.ui.fragments.ShowsFragment
import fr.iut.pm.movieapplication.utils.Constants
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import retrofit2.awaitResponse
import fr.iut.pm.movieapplication.ui.fragments.TvShowsFragment
class MainActivity : AppCompatActivity() {
val movieRepository : MovieRepository = MovieRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
@ -42,31 +33,17 @@ class MainActivity : AppCompatActivity() {
}
R.id.movies_page -> {
loadFragments(MoviesFragment(this))
loadFragments(MoviesFragment())
return@setOnItemSelectedListener true
}
R.id.series_page -> {
loadFragments(ShowsFragment(this))
loadFragments(TvShowsFragment())
return@setOnItemSelectedListener true
}
else -> false
}
}
/*
//Trends fragment injected in main activity
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, HomeSectionsFragment())
transaction.addToBackStack(null)
transaction.commit()
*/
if(Build.VERSION.SDK_INT < 33) {
// Hide the status bar.
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
// Remember that you should never show the action bar if the
// status bar is hidden, so hide that too if necessary.
actionBar?.hide()
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
@ -78,12 +55,13 @@ class MainActivity : AppCompatActivity() {
searchView.setOnQueryTextListener( object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
TODO("Not yet implemented")
return false
if(!query.isNullOrEmpty()) {
}
return true;
}
override fun onQueryTextChange(newText: String?): Boolean {
TODO("Not yet implemented")
return false
}

@ -1,12 +0,0 @@
package fr.iut.pm.movieapplication.ui.adapter
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class CategoryItemDecoration : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
outRect.bottom = 50
}
}

@ -1,52 +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.api.config.GlobalImageConfig
import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.utils.Constants.Companion.IMG_URL
class HomeItemAdapter(
private val context: MainActivity,
private val layoutId: Int,
private val list: List<Movie>
) : RecyclerView.Adapter<HomeItemAdapter.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): ViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(layoutId, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = list[position]
Log.d("SINGLETON", GlobalImageConfig.baseUrl)
val imgUri = currentItem.posterPath?.let {
(IMG_URL + it).toUri().buildUpon().scheme("https").build()
}
Log.d("SINGLETON", imgUri.toString() )
holder.itemImage.load(imgUri)
holder.itemName.text = currentItem.title
holder.itemDate.text = currentItem.releaseDate
}
override fun getItemCount(): Int = list.size
}

@ -0,0 +1,40 @@
package fr.iut.pm.movieapplication.ui.adapter
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
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.databinding.ItemHorizontalHomePageBinding
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection
import fr.iut.pm.movieapplication.utils.Constants
class MediaAdapter(private val listener : MovieSelection): ListAdapter<MediaResult, MediaAdapter.ViewHolder>(DiffUtilMediaCallback) {
private object DiffUtilMediaCallback : DiffUtil.ItemCallback<MediaResult>() {
override fun areItemsTheSame(oldItem: MediaResult, newItem: MediaResult) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: MediaResult, newItem: MediaResult) = oldItem == newItem
}
class ViewHolder(private val binding : ItemHorizontalHomePageBinding, listener: MovieSelection)
: RecyclerView.ViewHolder(binding.root) {
val mediaResult : MediaResult? get() = binding.mediaResult
fun bind(mediaResult : MediaResult) {
binding.mediaResult = mediaResult
val imgUri = mediaResult.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(ItemHorizontalHomePageBinding.inflate(LayoutInflater.from(parent.context)), listener)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position))
}

@ -0,0 +1,45 @@
package fr.iut.pm.movieapplication.ui.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
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.databinding.ItemMovieCategoryBinding
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.ui.dialog.MovieDialog
import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection
import fr.iut.pm.movieapplication.utils.Constants
class MovieAdapter(private val listener : MovieSelection) : ListAdapter<Movie, MovieAdapter.ViewHolder>(DiffUtilMovieCallback) {
private object DiffUtilMovieCallback : 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, listener: MovieSelection) :
RecyclerView.ViewHolder(binding.root) {
val movie : Movie? get() = binding.movie
init {
itemView.setOnClickListener {
listener.onMovieSelected(movie?.id ?: 0)
}
}
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)), listener)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position))
}

@ -0,0 +1,4 @@
package fr.iut.pm.movieapplication.ui.adapter
class SearchResultAdapter {
}

@ -0,0 +1,48 @@
package fr.iut.pm.movieapplication.ui.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
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.databinding.ItemTvShowCategoryBinding
import fr.iut.pm.movieapplication.model.media.tvshow.TvShow
import fr.iut.pm.movieapplication.utils.Constants
class TvShowAdapter() : ListAdapter<TvShow, TvShowAdapter.ViewHolder>(DiffUtilTvShowCallback) {
private object DiffUtilTvShowCallback : DiffUtil.ItemCallback<TvShow>() {
override fun areItemsTheSame(oldItem: TvShow, newItem: TvShow) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: TvShow, newItem: TvShow) = oldItem == newItem
}
class ViewHolder(private val binding : ItemTvShowCategoryBinding)
: RecyclerView.ViewHolder(binding.root){
val tvShow : TvShow? get() = binding.tvShow
init {
itemView.setOnClickListener {}
}
fun bind(tvShow : TvShow) {
binding.tvShow = tvShow
val imgUri = tvShow.posterPath?.let { (Constants.IMG_URL +it).toUri().buildUpon().scheme("https").build() }
binding.itemImage.load(imgUri)
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TvShowAdapter.ViewHolder =
TvShowAdapter.ViewHolder(ItemTvShowCategoryBinding.inflate(LayoutInflater.from(parent.context)))
override fun onBindViewHolder(holder: TvShowAdapter.ViewHolder, position: Int) = holder.bind(getItem(position))
}

@ -0,0 +1,42 @@
package fr.iut.pm.movieapplication.ui.dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.net.toUri
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.viewModels
import coil.load
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.databinding.MovieDialogBinding
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesDialogVM
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesDialogVMFactory
import fr.iut.pm.movieapplication.utils.Constants
class MovieDialog : DialogFragment() {
val moviesDialogVM by viewModels<MoviesDialogVM> { MoviesDialogVMFactory(arguments?.getInt("movieId") ?: 0) }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = MovieDialogBinding.inflate(inflater)
binding.lifecycleOwner = viewLifecycleOwner
binding.closeItem.setOnClickListener { dismiss() }
moviesDialogVM.getMovieDetailLiveData().observe(viewLifecycleOwner) {
binding.movieDetails = it
binding.detailsImage.load(it.posterPath?.let { it ->
(Constants.IMG_URL + it).toUri().buildUpon().scheme("https").build()
})
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}

@ -5,41 +5,70 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.model.Movie
import androidx.fragment.app.viewModels
import fr.iut.pm.movieapplication.databinding.FragmentHomeSectionsBinding
import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.ui.adapter.HomeItemAdapter
import fr.iut.pm.movieapplication.ui.adapter.MediaAdapter
import fr.iut.pm.movieapplication.ui.adapter.HomeItemDecoration
import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection
import fr.iut.pm.movieapplication.ui.viewmodel.HomeSectionsVM
class HomeSectionsFragment(
private val context : MainActivity
) : Fragment() {
) : Fragment(), MovieSelection {
private lateinit var homeSectionsViewModel : HomeSectionsVM
private val homeSectionsVM by viewModels<HomeSectionsVM>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_home_sections, container, false)
private val trendsAdapter = MediaAdapter(this)
private val popularMoviesAdapter = MediaAdapter(this)
private val popularTvShowsAdapter = MediaAdapter(this)
//get the trends RecyclerView
context.movieRepository.getTrends {
val homeTrendsRecyclerView = view?.findViewById<RecyclerView>(R.id.home_trends_recycler_view)
homeTrendsRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,it)
homeTrendsRecyclerView?.addItemDecoration(HomeItemDecoration())
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?)
: View? {
val binding = FragmentHomeSectionsBinding.inflate(inflater)
binding.homeSectionsVM = homeSectionsVM
binding.lifecycleOwner = viewLifecycleOwner
with(binding.homeTrendsRecyclerView) {
adapter = trendsAdapter
addItemDecoration(HomeItemDecoration())
}
//get the popularity RecyclerView
context.movieRepository.getPopularMovies {
val homePopularityRecyclerView = view?.findViewById<RecyclerView>(R.id.home_popularity_recycler_view)
homePopularityRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,it)
homePopularityRecyclerView?.addItemDecoration(HomeItemDecoration())
with(binding.homePopularMoviesRecyclerView) {
adapter = popularMoviesAdapter
addItemDecoration(HomeItemDecoration())
}
//get the free RecyclerView
val homeFreeRecyclerView = view?.findViewById<RecyclerView>(R.id.home_free_recycler_view)
homeFreeRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,ArrayList())
homeFreeRecyclerView?.addItemDecoration(HomeItemDecoration())
return view
with(binding.homePopularTvShowsRecyclerView) {
adapter = popularTvShowsAdapter
addItemDecoration(HomeItemDecoration())
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeSectionsVM.getTrendsLiveData().observe(viewLifecycleOwner) {
trendsAdapter.submitList(it)
}
homeSectionsVM.getPopularMoviesLiveData().observe(viewLifecycleOwner) {
popularMoviesAdapter.submitList(it)
}
homeSectionsVM.getPopularTvShowsLiveData().observe(viewLifecycleOwner) {
popularTvShowsAdapter.submitList(it)
}
}
override fun onMovieSelected(movieId: Int) {
TODO("Not yet implemented")
}
}

@ -4,39 +4,76 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.ui.adapter.CategoryItemDecoration
import fr.iut.pm.movieapplication.ui.adapter.HomeItemAdapter
import fr.iut.pm.movieapplication.databinding.FragmentMoviesBinding
import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter
import fr.iut.pm.movieapplication.ui.dialog.MovieDialog
import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVM
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVMFactory
import fr.iut.pm.movieapplication.ui.viewmodel.viewModelFactory
import kotlinx.coroutines.launch
class MoviesFragment(
private val context : MainActivity
) : Fragment() {
private val moviesVM: MoviesVM by viewModels{ MoviesVMFactory(repository = MovieRepository())}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_movies, container, false)
context.movieRepository.getPopularMovies { listMovies ->
val moviesRecyclerView = view?.findViewById<RecyclerView>(R.id.movies_item_recycler_view)
moviesRecyclerView?.adapter = HomeItemAdapter(context, R.layout.item_vertical_fragment, listMovies)
moviesRecyclerView ?. layoutManager = GridLayoutManager (context, 2)
moviesRecyclerView?.addItemDecoration(CategoryItemDecoration())
) : Fragment(), MovieSelection {
private val moviesVM by viewModels<MoviesVM>()
val moviesAdapter = MovieAdapter(this)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentMoviesBinding.inflate(inflater)
binding.moviesVM = moviesVM
binding.lifecycleOwner = viewLifecycleOwner
with(binding.moviesItemRecyclerView) {
adapter = moviesAdapter
}
val adapter = ArrayAdapter.createFromResource(
requireContext(),
R.array.movie_filter,
android.R.layout.simple_spinner_item
)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
with(binding.categoryMovieSpinner)
{
this.adapter = adapter
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
moviesVM.getData(selectedItem.toString())
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
moviesVM.getMoviesLiveData().observe(viewLifecycleOwner) {
moviesAdapter.submitList(it)
}
}
return view
override fun onMovieSelected(movieId: Int) {
val dialog = MovieDialog()
val args = Bundle()
args.putInt("movieId",movieId)
dialog.arguments = args
dialog.show(parentFragmentManager, "tag")
}
}
}

@ -0,0 +1,23 @@
package fr.iut.pm.movieapplication.ui.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import fr.iut.pm.movieapplication.ui.viewmodel.SearchResultVM
import fr.iut.pm.movieapplication.ui.viewmodel.SearchResultVMFactory
class SearchResultFragment : Fragment() {
private val searchResultViewModel by viewModels<SearchResultVM> { SearchResultVMFactory(arguments?.getString("query")!!) }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}
}

@ -1,15 +0,0 @@
package fr.iut.pm.movieapplication.ui.fragments
import android.os.Bundle
import androidx.fragment.app.Fragment
import fr.iut.pm.movieapplication.ui.activity.MainActivity
class ShowsFragment(
private val context : MainActivity
) : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}

@ -0,0 +1,68 @@
package fr.iut.pm.movieapplication.ui.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.databinding.FragmentTvShowsBinding
import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.ui.viewmodel.TvShowVM
class TvShowsFragment(
) : Fragment() {
private val tvShowVM by viewModels<TvShowVM>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?)
: View {
val binding = FragmentTvShowsBinding.inflate(inflater)
binding.tvShowVM = tvShowVM
binding.lifecycleOwner = viewLifecycleOwner
val adapter = ArrayAdapter.createFromResource(
requireContext(),
R.array.tv_show_filter,
android.R.layout.simple_spinner_item
)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
with(binding.categoryTvShowSpinner)
{
this.adapter = adapter
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
tvShowVM.getData(selectedItem.toString())
}
override fun onNothingSelected(parent: AdapterView<*>?) {
TODO("Not yet implemented")
}
}
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
tvShowVM.getTvShowLiveData().observe(viewLifecycleOwner) {
tvShowVM.tvShowAdapter.submitList(it)
}
}
}

@ -0,0 +1,6 @@
package fr.iut.pm.movieapplication.ui.interfaces
interface MovieSelection {
fun onMovieSelected(movieId : Int)
}

@ -1,4 +1,33 @@
package fr.iut.pm.movieapplication.ui.viewmodel
class HomeSectionsVM {
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.repository.MediaRepository
import kotlinx.coroutines.launch
class HomeSectionsVM : ViewModel() {
private val repository = MediaRepository()
private var _trendsLiveData : MutableLiveData<List<MediaResult>> = MutableLiveData()
fun getTrendsLiveData() : MutableLiveData<List<MediaResult>> = _trendsLiveData
private var _popularMoviesLiveData : MutableLiveData<List<MediaResult>> = MutableLiveData()
fun getPopularMoviesLiveData() : MutableLiveData<List<MediaResult>> = _popularMoviesLiveData
private var _popularTvShowsLiveData : MutableLiveData<List<MediaResult>> = MutableLiveData()
fun getPopularTvShowsLiveData() : MutableLiveData<List<MediaResult>> = _popularTvShowsLiveData
init {
viewModelScope.launch {
_trendsLiveData.postValue(repository.getTrends())
_popularMoviesLiveData.postValue(repository.getPopularMovies())
_popularTvShowsLiveData.postValue(repository.getPopularTvShows())
}
}
}

@ -0,0 +1,29 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.*
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import fr.iut.pm.movieapplication.repository.MovieRepository
import kotlinx.coroutines.launch
class MoviesDialogVM(private val movieId : Int) : ViewModel() {
private val repository = MovieRepository()
private var _movieDetailsLiveData : MutableLiveData<MovieDetails> = MutableLiveData()
fun getMovieDetailLiveData() : LiveData<MovieDetails> = _movieDetailsLiveData
init {
viewModelScope.launch {
if(movieId != 0) _movieDetailsLiveData.postValue(repository.getMovieDetails(movieId))
}
}
}
class MoviesDialogVMFactory(private val movieId : Int) : ViewModelProvider.Factory
{
override fun<T:ViewModel>create(modelClass:Class<T>) : T
{
return MoviesDialogVM(movieId) as T
}
}

@ -1,37 +1,111 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.*
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.model.Movie
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 kotlinx.coroutines.CoroutineScope
import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter
import fr.iut.pm.movieapplication.utils.Constants.Companion.MAX_PAGE
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MoviesVM(private val repository: MovieRepository) : ViewModel() {
class MoviesVM() : ViewModel() {
/**
* The movie repository used to get our data
*/
private val repository = MovieRepository()
private val _popularMovies = MutableLiveData<List<Movie>>()
val popularMovies : LiveData<List<Movie>> = _popularMovies
/**
* The MutableLiveData
*/
private var _moviesLiveData : MutableLiveData<List<Movie>> = MutableLiveData<List<Movie>>()
init {
//loadData()
/**
* Getter of the LiveData
*/
fun getMoviesLiveData() : LiveData<List<Movie>> = _moviesLiveData
/**
* The current data filter
*/
private var currentFilter = ""
/**
* The adapter of the RecyclerView (set on the RecyclerView in the view)
*/
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
if(lastVisibleItemPosition == totalItemCount -1) {
++currentPage
//1000 is the MAX_PAGE
if(currentPage <= MAX_PAGE) getMoreData(currentPage)
}
}
}
suspend fun loadData() {
viewModelScope.launch {
repository.getPopularMovies { movies ->
_popularMovies.value = movies
/**
* Currrent page where the data are obtained
*/
private var currentPage = 1
/**
* Get the data with a given filter
* @param filter filter applied to get data
*/
fun getData(filter : String) {
currentFilter = filter
currentPage = 1
when(currentFilter) {
"Populaires" -> viewModelScope.launch {
_moviesLiveData.postValue(repository.getPopularMovies())
repository.getMovieDetails(315162)
}
}.join()
"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())
}
}
}
}
/**
* Get more data with the actual filter
* @param page page from which the data are obtained
*/
private 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 {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MoviesVM(repository) as T
}
}

@ -0,0 +1,23 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class SearchResultVM(private var query : String) : ViewModel() {
private var _queryLiveData : MutableLiveData<String> = MutableLiveData()
fun getQueryLiveData() : LiveData<String> = _queryLiveData
init {
_queryLiveData.postValue(query)
}
}
class SearchResultVMFactory(private var query : String) : ViewModelProvider.Factory
{
override fun<T:ViewModel>create(modelClass:Class<T>) : T
{
return SearchResultVM(query) as T
}
}

@ -0,0 +1,43 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import fr.iut.pm.movieapplication.model.media.tvshow.TvShow
import fr.iut.pm.movieapplication.repository.TvShowRepository
import fr.iut.pm.movieapplication.ui.adapter.TvShowAdapter
import kotlinx.coroutines.launch
class TvShowVM : ViewModel() {
private val tvShowRepository = TvShowRepository()
private var tvShowLiveData : MutableLiveData<List<TvShow>> = MutableLiveData<List<TvShow>>()
fun getTvShowLiveData() : LiveData<List<TvShow>> = tvShowLiveData
private var currentFilter : String = ""
val tvShowAdapter = TvShowAdapter()
private var currentPage = 1
fun getData(filter : String) {
currentFilter = filter
currentPage = 1
when(currentFilter) {
"Populaires" -> viewModelScope.launch {
tvShowLiveData.postValue(tvShowRepository.getPopularTvShows())
}
"Diffusées aujourd\'hui" -> viewModelScope.launch {
tvShowLiveData.postValue(tvShowRepository.getAiringTodayTvShows())
}
"En cours de diffusion" -> viewModelScope.launch {
tvShowLiveData.postValue(tvShowRepository.getTvOnTheAirTvShows())
}
"Les mieux notées" -> viewModelScope.launch {
tvShowLiveData.postValue(tvShowRepository.getTopRatedTvShows())
}
}
}
}

@ -1,9 +0,0 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) =
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T = f() as T
}

@ -3,8 +3,15 @@ package fr.iut.pm.movieapplication.utils
class Constants {
companion object {
//API
const val BASE_URL = "https://api.themoviedb.org/3/"
const val IMG_URL = "https://image.tmdb.org/t/p/w500"
const val API_KEY = "8f14a279249638d7f247d0d7298b21b4"
//VIEW PAGINATION
const val PAGE_SIZE = 20
const val MAX_PAGE = 1000
}
}

@ -1,36 +0,0 @@
package fr.iut.pm.movieapplication.utils
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.model.Movie
object Mapper {
fun MapToMovie( movieDTO : MovieResultDTO) : Movie {
return Movie(
adult = movieDTO.adult,
posterPath = movieDTO.posterPath,
overview = movieDTO.overview,
releaseDate = movieDTO.releaseDate,
movieId = movieDTO.id,
originalTitle = movieDTO.originalTitle,
originalLanguage = movieDTO.originalLanguage,
title = movieDTO.title,
backdropPath = movieDTO.backdropPath,
popularity = movieDTO.popularity,
voteCount = movieDTO.voteCount,
voteAverage = movieDTO.voteAverage,
budget = null,
genres = null,
homePage = null,
productionCompanies = null,
productionCountries = null,
revenue = null,
runtime = null,
status = null,
tagLine = null
)
}
}

@ -0,0 +1,103 @@
package fr.iut.pm.movieapplication.utils
import fr.iut.pm.movieapplication.api.dtos.GenreDTO
import fr.iut.pm.movieapplication.api.dtos.MediaResultDTO
import fr.iut.pm.movieapplication.api.dtos.MovieDetailsDTO
import fr.iut.pm.movieapplication.model.Genre
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import fr.iut.pm.movieapplication.model.media.tvshow.TvShow
object MediaResultMapper {
fun mapToTvShow(mediaResultDTO: MediaResultDTO): TvShow {
return TvShow(
posterPath = mediaResultDTO.posterPath,
popularity = mediaResultDTO.popularity,
id = mediaResultDTO.id,
backdropPath = mediaResultDTO.backdropPath ?: "",
voteAverage = mediaResultDTO.voteAverage,
overview = mediaResultDTO.overview ?: "",
firstAirDate = mediaResultDTO.firstAirDate,
originCountry = mediaResultDTO.originCountry!!,
// genreIds = mediaResultDTO.genreIds,
originalLanguage = mediaResultDTO.originalLanguage,
voteCount = mediaResultDTO.voteCount,
name = mediaResultDTO.name!!,
originalName = mediaResultDTO.originalName!!
)
}
fun mapToMovie(mediaResultDTO: MediaResultDTO): Movie {
return Movie(
posterPath = mediaResultDTO.posterPath,
adult = mediaResultDTO.adult,
overview = mediaResultDTO.overview ?: "",
releaseDate = mediaResultDTO.releaseDate ?: "",
// genreIds = mediaResultDTO.genreIds,
id = mediaResultDTO.id,
originalTitle = mediaResultDTO.originalTitle!!,
originalLanguage = mediaResultDTO.originalLanguage,
title = mediaResultDTO.title!!,
backdropPath = mediaResultDTO.backdropPath,
popularity = mediaResultDTO.popularity,
voteCount = mediaResultDTO.voteCount,
//video = mediaResultDTO.video,
voteAverage = mediaResultDTO.voteAverage
)
}
fun mapToMediaResult(mediaResultDTO: MediaResultDTO) : MediaResult {
return MediaResult(
posterPath = mediaResultDTO.posterPath,
adult = mediaResultDTO.adult,
overview = mediaResultDTO.overview ?: "",
releaseDate = mediaResultDTO.releaseDate ?: mediaResultDTO.firstAirDate!! ,
originCountry = mediaResultDTO.originCountry,
// genreIds = mediaResultDTO.genreIds,
id = mediaResultDTO.id,
originalTitle = mediaResultDTO.originalTitle ?: mediaResultDTO.originalName!!, //if it's not a movie also it's a tvshow
originalLanguage = mediaResultDTO.originalLanguage,
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,
mediaType = mediaResultDTO.mediaType
)
}
fun mapToMovieDetails(movieDetailsDTO: MovieDetailsDTO?): MovieDetails? {
if(movieDetailsDTO == null) return null
return MovieDetails(
posterPath = movieDetailsDTO.posterPath,
adult = movieDetailsDTO.adult!!,
overview = movieDetailsDTO.overview ?: "",
releaseDate = movieDetailsDTO.releaseDate ?: "",
id = movieDetailsDTO.id,
originalTitle = movieDetailsDTO.originalTitle!!,
originalLanguage = movieDetailsDTO.originalLanguage,
title = movieDetailsDTO.title,
backdropPath = movieDetailsDTO.backdropPath,
popularity = movieDetailsDTO.popularity,
voteCount = movieDetailsDTO.voteCount,
voteAverage = movieDetailsDTO.voteAverage,
budget = movieDetailsDTO.budget,
genres = movieDetailsDTO.genres.map { mapGenreDTOToGenre(it) },
homepage = movieDetailsDTO.homepage,
revenue = movieDetailsDTO.revenue,
status = movieDetailsDTO.status
)
}
fun mapGenreDTOToGenre(genreDTO : GenreDTO) : Genre {
return Genre(
name = genreDTO.name,
id = genreDTO.id
)
}
}

@ -0,0 +1,48 @@
package fr.iut.pm.movieapplication.utils
import fr.iut.pm.movieapplication.api.dtos.MovieDTO
import fr.iut.pm.movieapplication.api.dtos.MovieDetailsDTO
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
object MovieMapper {
fun mapToMovie(movieDTO : MovieDTO) : Movie {
return Movie(
posterPath = movieDTO.posterPath,
adult = movieDTO.adult,
overview = movieDTO.overview,
releaseDate = movieDTO.releaseDate,
id = movieDTO.id,
originalTitle = movieDTO.originalTitle,
originalLanguage = movieDTO.originalLanguage,
title = movieDTO.title,
backdropPath = movieDTO.backdropPath,
popularity = movieDTO.popularity,
voteCount = movieDTO.voteCount,
voteAverage = movieDTO.voteAverage
)
}
fun mapToMovieDetails(movieDetailsDTO: MovieDetailsDTO ) : MovieDetails {
return MovieDetails(
posterPath = movieDetailsDTO.posterPath,
adult = movieDetailsDTO.adult!!,
overview = movieDetailsDTO.overview ?: "",
releaseDate = movieDetailsDTO.releaseDate ?: "",
id = movieDetailsDTO.id,
originalTitle = movieDetailsDTO.originalTitle,
originalLanguage = movieDetailsDTO.originalLanguage,
title = movieDetailsDTO.title,
backdropPath = movieDetailsDTO.backdropPath,
popularity = movieDetailsDTO.popularity,
voteCount = movieDetailsDTO.voteCount,
voteAverage = movieDetailsDTO.voteAverage,
budget = movieDetailsDTO.budget,
genres = movieDetailsDTO.genres.map { MediaResultMapper.mapGenreDTOToGenre(it) },
homepage = movieDetailsDTO.homepage,
revenue = movieDetailsDTO.revenue,
status = movieDetailsDTO.status
)
}
}

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

@ -1,69 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/section_nested_scroll_view">
<data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<import type="android.view.View" />
<TextView
android:id="@+id/home_page_trends_section_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/home_page_trends_section_title"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle"
/>
<variable
name="homeSectionsVM"
type="fr.iut.pm.movieapplication.ui.viewmodel.HomeSectionsVM" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_trends_recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</data>
<TextView
android:id="@+id/home_page_popularity_section_title"
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/home_page_popularity_section_title"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" />
android:contentDescription="@string/section_nested_scroll_view">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_popularity_recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<TextView
android:id="@+id/home_page_free_section_title"
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/home_page_free_section_title"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" />
>
<TextView
android:id="@+id/home_page_trends_section_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/home_page_trends_section_title"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_trends_recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_vertical_fragment" />
<TextView
android:id="@+id/home_page_popularity_section_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/home_page_popular_movies_section_title"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_popular_movies_recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_vertical_fragment" />
<TextView
android:id="@+id/home_page_free_section_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/home_page_popular_tv_shows_section_title"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_popular_tv_shows_recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_free_recycler_view"
android:layout_width="match_parent"
android:layout_height="250dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</layout>

@ -1,14 +1,55 @@
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<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="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
/>
android:layout_height="match_parent"
android:orientation="vertical">
<Spinner
android:id="@+id/category_movie_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"
app:onScrollListener = "@{moviesVM.scrollListener}"
tools:listitem="@layout/item_movie_category"
/>
</LinearLayout>
</LinearLayout>
</layout>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<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="searchResultsVM"
type="fr.iut.pm.movieapplication.ui.viewmodel.SearchResultVM" />
</data>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_results_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"
tools:listitem="@layout/item_vertical_fragment" />
</LinearLayout>
</layout>

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<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="tvShowVM"
type="fr.iut.pm.movieapplication.ui.viewmodel.TvShowVM" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Spinner
android:id="@+id/category_tv_show_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/tv_shows_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="@{tvShowVM.tvShowAdapter}"
tools:listitem="@layout/item_tv_show_category"
/>
</LinearLayout>
</layout>

@ -1,45 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<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"
android:layout_width="150dp"
android:layout_height="match_parent"
android:layout_marginTop="10dp">
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="200dp"
app:cardCornerRadius="5dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/item_image"
android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="mediaResult"
type="fr.iut.pm.movieapplication.model.media.MediaResult" />
</data>
<LinearLayout
android:layout_width="150dp"
android:layout_height="match_parent"
android:orientation="vertical" >
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="150dp"
android:layout_height="match_parent"
android:background="@color/black"
android:scaleType="centerCrop" />
app:cardCornerRadius="5dp">
<ImageView
android:id="@+id/item_image"
android:layout_width="match_parent"
android:layout_height="210dp"
android:background="@color/black"
android:scaleType="centerCrop"
android:contentDescription="description" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cardView" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/item_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/item_name" />
<TextView
android:id="@+id/item_name"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center"
android:textAlignment="center"
android:text="@{mediaResult.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="@{mediaResult.releaseDate}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</layout>

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="movie"
type="fr.iut.pm.movieapplication.model.media.movie.Movie" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
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="40dp"
android:layout_gravity="center"
android:textAlignment="center"
android:text="@{movie.title.length() > 20 ? movie.title.substring(0,20) + `...` : movie.title}"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</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="tvShow"
type="fr.iut.pm.movieapplication.model.media.tvshow.TvShow" />
</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="@{tvShow.name.length() > 20 ? tvShow.name.substring(0,20) + `...` : tvShow.name }"/>
<TextView
android:id="@+id/item_date"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center"
android:textAlignment="center"
android:text="@{tvShow.firstAirDate}"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

@ -1,46 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="175dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_marginTop="@dimen/default_margin"
app:layout_constraintTop_toTopOf="parent"
app:cardCornerRadius="10dp">
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:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/item_image"
android:layout_width="match_parent"
android:layout_height="210dp"
android:scaleType="centerCrop"
android:background="@color/black" />
<TextView
android:id="@+id/item_name"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center"
android:textAlignment="center"
/>
<TextView
android:id="@+id/item_date"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center"
android:textAlignment="center"
/>
<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" />
<TextView
android:id="@+id/item_date"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center"
android:textAlignment="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<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="movieDetails"
type="fr.iut.pm.movieapplication.model.media.movie.MovieDetails" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/default_margin">
<ImageView
android:id="@+id/close_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:src="@drawable/ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="Close" />
<ImageView
android:id="@+id/details_image"
android:layout_width="wrap_content"
android:layout_height="200dp"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:autoText="true"
android:hint="The title of the movie"
android:text="@{movieDetails.title}"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#000"
app:layout_constraintEnd_toStartOf="@+id/close_item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/overview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@{movieDetails.overview.length() > 150 ? movieDetails.overview.substring(0, 150) + `...` : movieDetails.overview}"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/details_image"
app:layout_constraintTop_toBottomOf="@+id/details_image" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Enregistrer"
android:gravity="center_horizontal|center_vertical"
app:layout_constraintBottom_toTopOf="@+id/button2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Voir détails"
android:gravity="center_horizontal|center_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/button"
app:layout_constraintStart_toStartOf="@+id/button" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

@ -10,8 +10,25 @@
<string name="home_page_title">Bienvenue,</string>
<string name="home_page_subtitle">Des millions de films, émissions télévisées et artistes…</string>
<string name="home_page_trends_section_title">Tendances</string>
<string name="home_page_popularity_section_title">Populaires</string>
<string name="home_page_free_section_title">Gratuits</string>
<string name="home_page_popular_movies_section_title">Films populaires</string>
<string name="home_page_popular_tv_shows_section_title">Séries populaires</string>
<string name="section_nested_scroll_view">section_nested_scroll_view</string>
<!-- Filter name for movie category -->
<!-- Category page -->
<string-array name="movie_filter">
<item>Populaires</item>
<item>Du moment</item>
<item>À venir</item>
<item>Les mieux notés</item>
</string-array>
<string-array name="tv_show_filter">
<item>Populaires</item>
<item>Diffusées aujourd\'hui</item>
<item>En cours de diffusion</item>
<item>Les mieux notées</item>
</string-array>
</resources>
Loading…
Cancel
Save