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

develop
Jordan ARTZET 2 years ago
commit 10646b7c38

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

@ -1,22 +1,51 @@
package fr.iut.pm.movieapplication.api 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.api.dtos.PopularDTO
import fr.iut.pm.movieapplication.utils.Constants.Companion.API_KEY import fr.iut.pm.movieapplication.utils.Constants.Companion.API_KEY
import retrofit2.Call import retrofit2.Call
import retrofit2.Response
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Path import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
interface MovieApplicationAPI { interface MovieApplicationAPI {
// Movie
@GET("movie/popular") @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}") @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") @GET("tv/{tv_id}")
fun getMovieDetails(@Path("movie_id") movieId : Int, @Query("api_key") apiKey: String = API_KEY) : Call<MovieResultDTO> 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 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 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 ( open 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,
@Json(name = "poster_path") @Json(name = "poster_path")
val posterPath: String?, open val posterPath: String?,
@Json(name = "production_countries") open val adult: Boolean,
val productionCountries: Array<ProductionCountry>, open val overview: String?,
@Json(name = "release_date") @Json(name = "release_date")
val releaseDate: String, open val releaseDate: String,
val revenue: Int, open val id: Int,
val runtime: Int?, @Json(name = "original_title")
//var spokenLanguages : Array<SpokenLanguage>, open val originalTitle: String,
val status: String, @Json(name = "original_language")
@Json(name = "tag_line") open val originalLanguage: String,
val tagLine: String?, open val title: String,
val title: String, @Json(name = "backdrop_path")
@Json(name = "vote_average") open val backdropPath: String?,
val voteAverage: Double, open val popularity: Double,
@Json(name = "vote_count") @Json(name = "vote_count")
val voteCount: Int, open val voteCount: Int,
val backdropPath: String? @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?, val popularity : Double?,
@Json(name = "vote_count") @Json(name = "vote_count")
val voteCount : Int?, val voteCount : Int?,
val video : Boolean?,
@Json(name = "vote_average") @Json(name = "vote_average")
val voteAverage : Double? val voteAverage : Double?

@ -9,7 +9,7 @@ class PopularDTO(
@Json(name = "page") @Json(name = "page")
val page : Int, val page : Int,
@Json(name = "results") @Json(name = "results")
val results : List<MovieResultDTO>, val results : List<MediaResultDTO>,
@Json(name = "total_results") @Json(name = "total_results")
val totalResults : Int, val totalResults : Int,
@Json(name = "total_pages") @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.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query 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 import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface MovieDAO { interface MovieDAO {
@Query("SELECT * FROM movies_table ORDER BY original_title ASC") @Query("SELECT * FROM movies_table ORDER BY original_title ASC")
fun getMovieByAlphabetizeMovie() : Flow<List<Movie>> fun getMovieByAlphabetizeMovie() : Flow<List<MovieDetails>>
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(movie : Movie) suspend fun insert(movie : MovieDetails)
@Query("DELETE FROM movies_table") @Query("DELETE FROM movies_table")
suspend fun deleteAll() suspend fun deleteAll()

@ -1,16 +1,7 @@
package fr.iut.pm.movieapplication.model 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( data class Genre(
@PrimaryKey val id : Int,
@ColumnInfo(name = "id") val name : String
var id : Int,
@ColumnInfo(name = "name")
var name : String
) { ) {
override fun equals(other: Any?): Boolean { 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.ColumnInfo
import androidx.room.Embedded import androidx.room.Embedded
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
data class Popular( data class Popular(
@ColumnInfo("page") @ColumnInfo("page")
val page : Int, val page : Int,
@ColumnInfo("results") @ColumnInfo("results")
@Embedded @Embedded
val results : List<Movie>, val results : List<MovieDetails>,
val totalResults : Int, val totalResults : Int,
val totalPages : 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 android.util.Log
import fr.iut.pm.movieapplication.api.RetrofitInstance import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.api.dtos.PopularDTO import fr.iut.pm.movieapplication.api.dtos.MovieDetailsDTO
import fr.iut.pm.movieapplication.model.Movie import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.utils.Constants.Companion.API_KEY import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import fr.iut.pm.movieapplication.utils.Mapper import fr.iut.pm.movieapplication.utils.MediaResultMapper
import retrofit2.Call import fr.iut.pm.movieapplication.utils.MovieMapper
import retrofit2.Callback import kotlinx.coroutines.Dispatchers
import retrofit2.Response import kotlinx.coroutines.withContext
class MovieRepository() { class MovieRepository {
suspend fun getPopularMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
fun getPopularMovies(callback: (List<Movie>) -> Unit ) { {
val listMovie : MutableList<Movie> = mutableListOf() val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getPopularMovies().enqueue(object : val response = RetrofitInstance.api.getPopularMovies(page = page)
Callback<PopularDTO> { if(response.isSuccessful) {
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) { val listMediaResultDTO = response.body()?.results
if (response.isSuccessful) { listMediaResultDTO?.forEach {
Log.d("List :", response.body().toString()) val movie = MediaResultMapper.mapToMovie(it)
val popularDTO = response.body() listMovie.add(movie)
val listMoviesDTO = popularDTO?.results Log.d("Movie ", movie.title!! + " " + movie.id)
listMoviesDTO?.forEach {
val movie = Mapper.MapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!!)
}
}
callback(listMovie)
} }
}
else Log.d("ERROR FAILED", response.message())
listMovie
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) { suspend fun getNowPlayingMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
Log.d("Error failure", t.message.toString()) {
}
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() val listMovie : MutableList<Movie> = mutableListOf()
RetrofitInstance.api.getTrending().enqueue(object : Callback<PopularDTO> { val response = RetrofitInstance.api.getUpcomingMovies(page = page)
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) { if(response.isSuccessful) {
if(response.isSuccessful) { val listMediaResultDTO = response.body()?.results
Log.d("Response",response.body().toString()) listMediaResultDTO?.forEach {
val popularDTO = response.body() val movie = MediaResultMapper.mapToMovie(it)
popularDTO?.results?.forEach { listMovie.add(movie)
val movie = Mapper.MapToMovie(it) Log.d("Movie ", movie.title!!)
listMovie.add(movie)
movie.title?.let { it1 -> Log.d("Movie", it1) }
}
}
callback(listMovie)
} }
}
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) { val response = RetrofitInstance.api.getTopRatedMovies(page = page)
Log.d("Error failure", t.message.toString()) 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.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import android.widget.SearchView.OnQueryTextListener
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import fr.iut.pm.movieapplication.R import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.api.RetrofitInstance import fr.iut.pm.movieapplication.repository.MediaRepository
import fr.iut.pm.movieapplication.api.config.GlobalImageConfig
import fr.iut.pm.movieapplication.repository.MovieRepository import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.ui.fragments.HomeSectionsFragment import fr.iut.pm.movieapplication.ui.fragments.HomeSectionsFragment
import fr.iut.pm.movieapplication.ui.fragments.MoviesFragment import fr.iut.pm.movieapplication.ui.fragments.MoviesFragment
import fr.iut.pm.movieapplication.ui.fragments.ShowsFragment import fr.iut.pm.movieapplication.ui.fragments.TvShowsFragment
import fr.iut.pm.movieapplication.utils.Constants
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import retrofit2.awaitResponse
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
val movieRepository : MovieRepository = MovieRepository()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
@ -42,31 +33,17 @@ class MainActivity : AppCompatActivity() {
} }
R.id.movies_page -> { R.id.movies_page -> {
loadFragments(MoviesFragment(this)) loadFragments(MoviesFragment())
return@setOnItemSelectedListener true return@setOnItemSelectedListener true
} }
R.id.series_page -> { R.id.series_page -> {
loadFragments(ShowsFragment(this)) loadFragments(TvShowsFragment())
return@setOnItemSelectedListener true return@setOnItemSelectedListener true
} }
else -> false 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 { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
@ -78,12 +55,13 @@ class MainActivity : AppCompatActivity() {
searchView.setOnQueryTextListener( object : SearchView.OnQueryTextListener { searchView.setOnQueryTextListener( object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String?): Boolean {
TODO("Not yet implemented") if(!query.isNullOrEmpty()) {
return false
}
return true;
} }
override fun onQueryTextChange(newText: String?): Boolean { override fun onQueryTextChange(newText: String?): Boolean {
TODO("Not yet implemented")
return false 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.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView import androidx.fragment.app.viewModels
import fr.iut.pm.movieapplication.R import fr.iut.pm.movieapplication.databinding.FragmentHomeSectionsBinding
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.ui.activity.MainActivity 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.adapter.HomeItemDecoration
import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection
import fr.iut.pm.movieapplication.ui.viewmodel.HomeSectionsVM import fr.iut.pm.movieapplication.ui.viewmodel.HomeSectionsVM
class HomeSectionsFragment( class HomeSectionsFragment(
private val context : MainActivity 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? { private val trendsAdapter = MediaAdapter(this)
val view = inflater.inflate(R.layout.fragment_home_sections, container, false) private val popularMoviesAdapter = MediaAdapter(this)
private val popularTvShowsAdapter = MediaAdapter(this)
//get the trends RecyclerView override fun onCreateView(
context.movieRepository.getTrends { inflater: LayoutInflater, container: ViewGroup?,
val homeTrendsRecyclerView = view?.findViewById<RecyclerView>(R.id.home_trends_recycler_view) savedInstanceState: Bundle?)
homeTrendsRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,it) : View? {
homeTrendsRecyclerView?.addItemDecoration(HomeItemDecoration())
val binding = FragmentHomeSectionsBinding.inflate(inflater)
binding.homeSectionsVM = homeSectionsVM
binding.lifecycleOwner = viewLifecycleOwner
with(binding.homeTrendsRecyclerView) {
adapter = trendsAdapter
addItemDecoration(HomeItemDecoration())
} }
//get the popularity RecyclerView with(binding.homePopularMoviesRecyclerView) {
context.movieRepository.getPopularMovies { adapter = popularMoviesAdapter
val homePopularityRecyclerView = view?.findViewById<RecyclerView>(R.id.home_popularity_recycler_view) addItemDecoration(HomeItemDecoration())
homePopularityRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,it)
homePopularityRecyclerView?.addItemDecoration(HomeItemDecoration())
} }
//get the free RecyclerView
val homeFreeRecyclerView = view?.findViewById<RecyclerView>(R.id.home_free_recycler_view) with(binding.homePopularTvShowsRecyclerView) {
homeFreeRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,ArrayList()) adapter = popularTvShowsAdapter
homeFreeRecyclerView?.addItemDecoration(HomeItemDecoration()) addItemDecoration(HomeItemDecoration())
return view }
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.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels 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.R
import fr.iut.pm.movieapplication.api.RetrofitInstance import fr.iut.pm.movieapplication.databinding.FragmentMoviesBinding
import fr.iut.pm.movieapplication.model.Movie import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter
import fr.iut.pm.movieapplication.repository.MovieRepository import fr.iut.pm.movieapplication.ui.dialog.MovieDialog
import fr.iut.pm.movieapplication.ui.activity.MainActivity import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection
import fr.iut.pm.movieapplication.ui.adapter.CategoryItemDecoration
import fr.iut.pm.movieapplication.ui.adapter.HomeItemAdapter
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVM 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( class MoviesFragment(
private val context : MainActivity ) : Fragment(), MovieSelection {
) : Fragment() {
private val moviesVM by viewModels<MoviesVM>()
private val moviesVM: MoviesVM by viewModels{ MoviesVMFactory(repository = MovieRepository())} val moviesAdapter = MovieAdapter(this)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(
val view = inflater.inflate(R.layout.fragment_movies, container, false) inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
context.movieRepository.getPopularMovies { listMovies -> ): View {
val moviesRecyclerView = view?.findViewById<RecyclerView>(R.id.movies_item_recycler_view) val binding = FragmentMoviesBinding.inflate(inflater)
moviesRecyclerView?.adapter = HomeItemAdapter(context, R.layout.item_vertical_fragment, listMovies) binding.moviesVM = moviesVM
moviesRecyclerView ?. layoutManager = GridLayoutManager (context, 2) binding.lifecycleOwner = viewLifecycleOwner
moviesRecyclerView?.addItemDecoration(CategoryItemDecoration())
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 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 package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.* import androidx.lifecycle.*
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO import androidx.recyclerview.widget.GridLayoutManager
import fr.iut.pm.movieapplication.model.Movie 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.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 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 -> * Currrent page where the data are obtained
_popularMovies.value = movies */
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 { class Constants {
companion object { companion object {
//API
const val BASE_URL = "https://api.themoviedb.org/3/" const val BASE_URL = "https://api.themoviedb.org/3/"
const val IMG_URL = "https://image.tmdb.org/t/p/w500" const val IMG_URL = "https://image.tmdb.org/t/p/w500"
const val API_KEY = "8f14a279249638d7f247d0d7298b21b4" 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"?> <?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" <data>
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">
<LinearLayout <import type="android.view.View" />
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView <variable
android:id="@+id/home_page_trends_section_title" name="homeSectionsVM"
android:layout_width="match_parent" type="fr.iut.pm.movieapplication.ui.viewmodel.HomeSectionsVM" />
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 </data>
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" />
<TextView <androidx.core.widget.NestedScrollView
android:id="@+id/home_page_popularity_section_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/home_page_popularity_section_title" android:contentDescription="@string/section_nested_scroll_view">
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 <LinearLayout
android:id="@+id/home_popularity_recycler_view" android:orientation="vertical"
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"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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" <TextView
android:layout_marginTop="@dimen/default_margin" android:id="@+id/home_page_trends_section_title"
style="@style/SectionTitleStyle" /> 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"?> <?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 <layout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/movies_item_recycler_view" 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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginBottom="@dimen/default_margin" 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"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
android:layout_width="150dp"
android:layout_height="match_parent" <data>
android:layout_marginTop="10dp"> <variable
name="mediaResult"
<androidx.cardview.widget.CardView type="fr.iut.pm.movieapplication.model.media.MediaResult" />
android:id="@+id/cardView" </data>
android:layout_width="match_parent"
android:layout_height="200dp" <LinearLayout
app:cardCornerRadius="5dp" android:layout_width="150dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent" android:orientation="vertical" >
app:layout_constraintTop_toTopOf="parent">
<androidx.cardview.widget.CardView
<ImageView android:id="@+id/cardView"
android:id="@+id/item_image" android:layout_width="150dp"
android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" app:cardCornerRadius="5dp">
android:scaleType="centerCrop" />
<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 </androidx.cardview.widget.CardView>
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" />
<TextView <TextView
android:id="@+id/item_date" android:id="@+id/item_name"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="20dp"
app:layout_constraintEnd_toEndOf="parent" android:layout_gravity="center"
app:layout_constraintStart_toStartOf="parent" android:textAlignment="center"
app:layout_constraintTop_toBottomOf="@+id/item_name" /> 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"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="175dp" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="250dp" android:layout_height="match_parent"
android:layout_marginLeft="10dp" android:layout_marginStart="4dp"
android:layout_marginStart="10dp" android:layout_marginLeft="4dp"
android:layout_marginEnd="10dp" android:layout_marginTop="8dp"
android:layout_marginTop="@dimen/default_margin" android:layout_marginEnd="4dp"
app:layout_constraintTop_toTopOf="parent" android:layout_marginRight="4dp"
app:cardCornerRadius="10dp"> 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 <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:id="@+id/item_image" android:id="@+id/item_image"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="210dp" android:layout_height="180dp"
android:scaleType="centerCrop" android:background="@color/black"
android:background="@color/black" /> android:scaleType="centerCrop" />
<TextView <TextView
android:id="@+id/item_name" android:id="@+id/item_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="20dp" android:layout_height="20dp"
android:layout_gravity="center" android:layout_gravity="center"
android:textAlignment="center" android:textAlignment="center" />
/>
<TextView
<TextView android:id="@+id/item_date"
android:id="@+id/item_date" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="20dp"
android:layout_height="20dp" android:layout_gravity="center"
android:layout_gravity="center" android:textAlignment="center" />
android:textAlignment="center"
/>
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </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_title">Bienvenue,</string>
<string name="home_page_subtitle">Des millions de films, émissions télévisées et artistes…</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_trends_section_title">Tendances</string>
<string name="home_page_popularity_section_title">Populaires</string> <string name="home_page_popular_movies_section_title">Films populaires</string>
<string name="home_page_free_section_title">Gratuits</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> <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> </resources>
Loading…
Cancel
Save