Compare commits

..

No commits in common. 'master' and 'features/retrofit/1' have entirely different histories.

@ -2,10 +2,7 @@
## Overview ## Overview
___ ___
This project use the API of [The Movie Database](https://www.themoviedb.org/?language=fr) Description of my project
This project is still in development :construction:
## Documentation ## Documentation
___ ___
@ -22,17 +19,7 @@ Here are the prerequisites before launching the project.
| ---- | ---- | | ---- | ---- |
| The integrated Development Environment (IDE) Android Studios |<img src="Documentation/images/Android_Studio_Trademark.png" alt="drawing" width="95" height="30" /> | | The integrated Development Environment (IDE) Android Studios |<img src="Documentation/images/Android_Studio_Trademark.png" alt="drawing" width="95" height="30" /> |
### Dependencies
- AndroidX
- Retrofit2 to use the api requests and moshi to parse data
- Coil for load the images
- Room Database to store local data
- The databinding
- JUnit is included too because i want to add tests on the project in the future
### Installation ### Installation
To launch the project, open `Android Studios` and click on `file` on the left corner. To launch the project, open `Android Studios` and click on `file` on the left corner.
Click on `open` and open the `Sources` folder in the project and let the IDE index the files Click on `open` and open the `Sources` folder in the project.

@ -8,14 +8,7 @@ android {
namespace 'fr.iut.pm.movieapplication' namespace 'fr.iut.pm.movieapplication'
compileSdk 33 compileSdk 33
def apikeyPropertiesFile = rootProject.file("apikey.properties")
def apikeyProperties = new Properties()
apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
defaultConfig { defaultConfig {
buildConfigField("String", "API_KEY", apikeyProperties['API_KEY'])
applicationId "fr.iut.pm.movieapplication" applicationId "fr.iut.pm.movieapplication"
minSdk 16 minSdk 16
targetSdk 33 targetSdk 33
@ -43,29 +36,24 @@ 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.fragment:fragment-ktx:1.5.5"
implementation "androidx.activity:activity-ktx:$rootProject.activityVersion" implementation "androidx.activity:activity-ktx:$rootProject.activityVersion"
implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion" implementation 'androidx.core:core-ktx:1.9.0'
implementation "com.google.android.material:material:$rootProject.materialVersion" implementation "androidx.fragment:fragment-ktx:1.5.5"
// Room components // Room components
implementation "androidx.room:room-runtime:$rootProject.roomVersion"
implementation "androidx.room:room-ktx:$rootProject.roomVersion" implementation "androidx.room:room-ktx:$rootProject.roomVersion"
kapt "androidx.room:room-compiler:$rootProject.roomVersion" kapt "androidx.room:room-compiler:$rootProject.roomVersion"
androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
// 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-runtime-ktx:$rootProject.lifecycleVersion" implementation "androidx.lifecycle:lifecycle-common-java8:$rootProject.lifecycleVersion"
// Kotlin components // Kotlin components
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -76,14 +64,19 @@ dependencies {
implementation "io.coil-kt:coil:1.1.1" implementation "io.coil-kt:coil:1.1.1"
// UI // UI
implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion"
implementation "com.google.android.material:material:$rootProject.materialVersion"
// 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 & Moshi // Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-scalars:2.9.0" // Retrofit with Scalar Converter
//implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-moshi:2.9.0" implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
// Testing // Testing

@ -5,7 +5,6 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:name=".MovieApplication"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
@ -14,10 +13,9 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.MovieApplication" android:theme="@style/Theme.MovieApplication"
tools:targetApi="33"> tools:targetApi="31">
<activity <activity
android:name=".ui.activity.MainActivity" android:name=".ui.activity.MainActivity"
android:screenOrientation="portrait"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

@ -1,12 +0,0 @@
package fr.iut.pm.movieapplication
import android.app.Application
import androidx.room.Room
import fr.iut.pm.movieapplication.data.database.MovieDataBase
class MovieApplication : Application() {
override fun onCreate() {
super.onCreate()
MovieDataBase.initialize(this)
}
}

@ -1,57 +1,22 @@
package fr.iut.pm.movieapplication.api package fr.iut.pm.movieapplication.api
import fr.iut.pm.movieapplication.api.dtos.MovieDetailsDTO import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.api.dtos.ResultDTO 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.Response import retrofit2.Call
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")
suspend fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<ResultDTO> fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY) : Call<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<ResultDTO>
@GET("movie/upcoming")
suspend fun getUpcomingMovies(@Query("api_key") apiKey: String = API_KEY, @Query("language") language : String = "fr", @Query("page") page : Int = 1) : Response<ResultDTO>
@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<ResultDTO>
// 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<ResultDTO>
@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<ResultDTO>
@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<ResultDTO>
@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<ResultDTO>
@GET("trending/{media_type}/{time_window}") @GET("trending/{media_type}/{time_window}")
suspend fun getTrending(@Path("media_type") mediaType : String = "all", @Path("time_window") timeWindow : String = "day", @Query("api_key") apiKey: String = API_KEY ) : Response<ResultDTO> fun getTrending(@Path("media_type") mediaType : String = "all", @Path("time_window") timeWindow : String = "day", @Query("api_key") apiKey: String = API_KEY ) : Call<PopularDTO>
@GET("search/multi")
suspend fun searchMedia(
@Query("query") query: String,
@Query("page") page: Int = 1,
@Query("api_key") apiKey: String = API_KEY,
@Query("language") language: String = "fr",
@Query("include_adult") includeAdult: Boolean = true
): Response<ResultDTO>
@GET("tv/{tv_id}") @GET("movie/{movie_id")
fun getShowDetails(@Path("tv_id") tvId : Int, @Query("api_key") apiKey: String = API_KEY) fun getMovieDetails(@Path("movie_id") movieId : Int, @Query("api_key") apiKey: String = API_KEY) : Call<MovieResultDTO>
} }

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

@ -1,42 +0,0 @@
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? = null,
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,29 +1,44 @@
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
open class MovieDTO ( 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")
open val posterPath: String?, val posterPath: String?,
open val adult: Boolean, @Json(name = "production_countries")
open val overview: String?, val productionCountries: Array<ProductionCountry>,
@Json(name = "release_date") @Json(name = "release_date")
open val releaseDate: String, val releaseDate: String,
open val id: Int, val revenue: Int,
@Json(name = "original_title") val runtime: Int?,
open val originalTitle: String, //var spokenLanguages : Array<SpokenLanguage>,
@Json(name = "original_language") val status: String,
open val originalLanguage: String, @Json(name = "tag_line")
open val title: String, val tagLine: String?,
@Json(name = "backdrop_path") val title: String,
open val backdropPath: String?,
open val popularity: Double,
@Json(name = "vote_count")
open val voteCount: Int,
@Json(name = "vote_average") @Json(name = "vote_average")
open val voteAverage: Double val voteAverage: Double,
@Json(name = "vote_count")
val voteCount: Int,
val backdropPath: String?
){ ){
} }

@ -1,37 +0,0 @@
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: Long,
//spoken language
val status: String,
val title: String,
@Json(name = "vote_average")
val voteAverage: Double,
@Json(name = "vote_count")
val voteCount: Int
)
{}

@ -28,6 +28,7 @@ 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?

@ -5,11 +5,11 @@ import com.squareup.moshi.Json
class ResultDTO( class PopularDTO(
@Json(name = "page") @Json(name = "page")
val page : Int, val page : Int,
@Json(name = "results") @Json(name = "results")
val results : List<MediaResultDTO>, val results : List<MovieResultDTO>,
@Json(name = "total_results") @Json(name = "total_results")
val totalResults : Int, val totalResults : Int,
@Json(name = "total_pages") @Json(name = "total_pages")

@ -1,30 +0,0 @@
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,
)
{}

@ -1,61 +1,23 @@
package fr.iut.pm.movieapplication.data.dao package fr.iut.pm.movieapplication.data.dao
import androidx.room.* import androidx.room.Dao
import fr.iut.pm.movieapplication.data.entities.* import androidx.room.Insert
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails import androidx.room.OnConflictStrategy
import androidx.room.Query
import fr.iut.pm.movieapplication.model.Movie
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface MovieDAO { interface MovieDAO {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Query("SELECT * FROM movies_table ORDER BY original_title ASC")
suspend fun insert(movieEntity : MovieEntity) fun getMovieByAlphabetizeMovie() : Flow<List<Movie>>
/**
* Get all movies from the movies_table
*/
@Query("SELECT * FROM movies_table")
suspend fun getAllMovies() : List<MovieEntity>
/**
* Get all movies from the movies_details_table
*/
@Query("SELECT * FROM movies_details_table")
suspend fun getAllMoviesDetails() : List<MovieDetailsEntity>
/**
* Get a movie by id
*/
@Query("SELECT * FROM movies_table WHERE id = :id")
fun getMovieById(id : Int) : MovieEntity
//delete a movie
@Query("DELETE FROM movies_table WHERE id = :id")
suspend fun deleteMovieById(id : Int)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMovieDetails(movieDetailsEntity: MovieDetailsEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertGenres(genres : List<GenreEntity>) suspend fun insert(movie : Movie)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertMovieDetailsGenres(movieDetailsGenresEntity : List<MovieDetailsGenreEntity>)
@Transaction
suspend fun insertMovieDetailsWithGenres(movieDetailsWithGenres : MovieDetailsWithGenres) {
insert(movieDetailsWithGenres.movieDetails.movie)
insertMovieDetails(movieDetailsWithGenres.movieDetails)
insertGenres(movieDetailsWithGenres.genres)
val movieDetailsGenres = movieDetailsWithGenres.genres.map {
MovieDetailsGenreEntity(movieDetailsWithGenres.movieDetails.movieId, it.genreId)
}
insertMovieDetailsGenres(movieDetailsGenres)
}
@Transaction @Query("DELETE FROM movies_table")
@Query("SELECT * FROM movies_details_table WHERE movie_id = :movieId") suspend fun deleteAll()
suspend fun getMovieDetailsWithGenresById(movieId: Int): MovieDetailsWithGenres
} }

@ -0,0 +1,9 @@
package fr.iut.pm.movieapplication.data.dao
import androidx.room.Entity
@Entity("movies_table")
class MovieEntity {
}

@ -1,60 +0,0 @@
package fr.iut.pm.movieapplication.data.database
import android.app.Application
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import fr.iut.pm.movieapplication.MovieApplication
import fr.iut.pm.movieapplication.data.dao.MovieDAO
import fr.iut.pm.movieapplication.data.entities.GenreEntity
import fr.iut.pm.movieapplication.data.entities.MovieDetailsEntity
import fr.iut.pm.movieapplication.data.entities.MovieDetailsGenreEntity
import fr.iut.pm.movieapplication.data.entities.MovieEntity
const val MOVIE_DB_NAME = "movies.db"
@Database( entities = [MovieEntity::class, MovieDetailsEntity::class, GenreEntity::class, MovieDetailsGenreEntity::class], version = 3)
abstract class MovieDataBase : RoomDatabase()
{
abstract fun movieDAO() : MovieDAO
companion object
{
private lateinit var application : Application
@Volatile
private var instance : MovieDataBase? = null
fun getInstance() : MovieDataBase
{
if (::application.isInitialized) {
if (instance == null)
synchronized(this)
{
if (instance == null)
instance = Room.databaseBuilder(
application.applicationContext,
MovieDataBase::class.java,
MOVIE_DB_NAME
)
.fallbackToDestructiveMigration()
.build()
}
return instance!!
}
else
throw RuntimeException("the database must be first initialized")
}
@Synchronized
fun initialize(app : MovieApplication)
{
if (::application.isInitialized)
throw RuntimeException("the database must not be initialized twice")
application = app
}
}
}

@ -1,12 +0,0 @@
package fr.iut.pm.movieapplication.data.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "genres_table")
data class GenreEntity(
@PrimaryKey @ColumnInfo("genre_id") val genreId : Int,
val name : String
) {
}

@ -1,19 +0,0 @@
package fr.iut.pm.movieapplication.data.entities
import androidx.room.*
import fr.iut.pm.movieapplication.model.Genre
@Entity(tableName = "movies_details_table")
data class MovieDetailsEntity(
@PrimaryKey @ColumnInfo("movie_id") val movieId : Int,
@Embedded val movie : MovieEntity,
val budget : Int,
val homepage : String?,
val revenue : Long,
val status : String
//Future attributes to add
) {
}

@ -1,17 +0,0 @@
package fr.iut.pm.movieapplication.data.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
@Entity(tableName = "movie_details_genre",
primaryKeys = ["movie_id", "genre_id"],
foreignKeys = [
ForeignKey(entity = MovieDetailsEntity::class, parentColumns = ["movie_id"], childColumns = ["movie_id"]),
ForeignKey(entity = GenreEntity::class, parentColumns = ["genre_id"], childColumns = ["genre_id"])
]
)
data class MovieDetailsGenreEntity(
@ColumnInfo("movie_id") val movieId : Int,
@ColumnInfo("genre_id") val genreId : Int
)

@ -1,15 +0,0 @@
package fr.iut.pm.movieapplication.data.entities
import androidx.room.Embedded
import androidx.room.Junction
import androidx.room.Relation
data class MovieDetailsWithGenres(
@Embedded val movieDetails: MovieDetailsEntity,
@Relation(
parentColumn = "movie_id",
entityColumn = "genre_id",
associateBy = Junction(MovieDetailsGenreEntity::class)
)
val genres: List<GenreEntity>
)

@ -1,23 +0,0 @@
package fr.iut.pm.movieapplication.data.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "movies_table")
data class MovieEntity(
@PrimaryKey val id: Int,
val title: String,
val overview: String?,
@ColumnInfo("poster_path") val posterPath: String?,
@ColumnInfo("backdrop_path") val backdropPath: String?,
@ColumnInfo("release_date") val releaseDate: String,
@ColumnInfo("vote_average") val voteAverage: Double,
@ColumnInfo("vote_count") val voteCount: Int,
val popularity: Double,
@ColumnInfo("original_language") val originalLanguage: String,
@ColumnInfo("original_title") val originalTitle: String,
val adult: Boolean,
@ColumnInfo("is_favorite") val isFavorite: Boolean
) {
}

@ -1,34 +0,0 @@
package fr.iut.pm.movieapplication.data.mapper
import fr.iut.pm.movieapplication.data.entities.GenreEntity
import fr.iut.pm.movieapplication.model.Genre
object GenreLocalMapper {
/**
* Map a [Genre] to a [GenreEntity]
*/
fun mapToGenreEntity(genre : Genre) : GenreEntity {
return GenreEntity(
genreId = genre.id,
name = genre.name
)
}
/**
* Map a list of [Genre] to a list of [GenreEntity]
*/
fun mapToGenreEntities(genres : List<Genre>) : List<GenreEntity> {
return genres.map { mapToGenreEntity(it) }
}
/**
* Map a [GenreEntity] to a [Genre]
*/
fun mapToGenre(genreEntity : GenreEntity) : Genre {
return Genre(
id = genreEntity.genreId,
name = genreEntity.name
)
}
}

@ -1,101 +0,0 @@
package fr.iut.pm.movieapplication.data.mapper
import fr.iut.pm.movieapplication.data.entities.MovieDetailsEntity
import fr.iut.pm.movieapplication.data.entities.MovieDetailsWithGenres
import fr.iut.pm.movieapplication.data.entities.MovieEntity
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
object MovieLocalMapper {
/**
* Map a [MovieEntity] to a [Movie]
*/
fun mapToMovie(movieEntity : MovieEntity) : Movie {
return Movie(
posterPath = movieEntity.posterPath,
adult = movieEntity.adult,
overview = movieEntity.overview,
releaseDate = movieEntity.releaseDate,
id = movieEntity.id,
originalTitle = movieEntity.originalTitle,
originalLanguage = movieEntity.originalLanguage,
title = movieEntity.title,
backdropPath = movieEntity.backdropPath,
popularity = movieEntity.popularity,
voteCount = movieEntity.voteCount,
voteAverage = movieEntity.voteAverage
)
}
/**
* Map a [Movie] to a [MovieEntity]
*/
fun mapToMovieEntity(movie : Movie) : MovieEntity {
return MovieEntity(
posterPath = movie.posterPath,
adult = movie.adult!!,
overview = movie.overview,
releaseDate = movie.releaseDate!!,
id = movie.id,
originalTitle = movie.originalTitle,
originalLanguage = movie.originalLanguage,
title = movie.title,
backdropPath = movie.backdropPath ,
popularity = movie.popularity,
voteCount = movie.voteCount,
voteAverage = movie.voteAverage,
isFavorite = false
)
}
/**
* Map a [MovieDetails] to a [MovieDetailsEntity]
*/
fun mapToMovieDetailsEntity(movieDetails : MovieDetails) : MovieDetailsEntity {
return MovieDetailsEntity(
movieId = movieDetails.id,
movie = mapToMovieEntity(movieDetails),
budget = movieDetails.budget,
homepage = movieDetails.homepage,
revenue = movieDetails.revenue,
status = movieDetails.status
)
}
/**
* Map a [MovieDetails] to a [MovieDetailsWithGenres]
*/
fun mapToMovieDetailsWithGenres(movieDetails: MovieDetails) : MovieDetailsWithGenres {
return MovieDetailsWithGenres(
movieDetails = mapToMovieDetailsEntity(movieDetails),
genres = movieDetails.genres.map { GenreLocalMapper.mapToGenreEntity(it) }
)
}
/**
* Map a [MovieDetailsWithGenres] to a [MovieDetails]
*/
fun mapToMovieDetails(movieDetailsWithGenres: MovieDetailsWithGenres) : MovieDetails {
return MovieDetails(
id = movieDetailsWithGenres.movieDetails.movieId,
posterPath = movieDetailsWithGenres.movieDetails.movie.posterPath,
adult = movieDetailsWithGenres.movieDetails.movie.adult,
overview = movieDetailsWithGenres.movieDetails.movie.overview,
releaseDate = movieDetailsWithGenres.movieDetails.movie.releaseDate,
originalTitle = movieDetailsWithGenres.movieDetails.movie.originalTitle,
originalLanguage = movieDetailsWithGenres.movieDetails.movie.originalLanguage,
title = movieDetailsWithGenres.movieDetails.movie.title,
backdropPath = movieDetailsWithGenres.movieDetails.movie.backdropPath,
popularity = movieDetailsWithGenres.movieDetails.movie.popularity,
voteCount = movieDetailsWithGenres.movieDetails.movie.voteCount,
voteAverage = movieDetailsWithGenres.movieDetails.movie.voteAverage,
budget = movieDetailsWithGenres.movieDetails.budget,
homepage = movieDetailsWithGenres.movieDetails.homepage,
revenue = movieDetailsWithGenres.movieDetails.revenue,
status = movieDetailsWithGenres.movieDetails.status,
genres = movieDetailsWithGenres.genres.map { GenreLocalMapper.mapToGenre(it) }
)
}
}

@ -1,7 +1,16 @@
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(
val id : Int, @PrimaryKey
val name : String @ColumnInfo(name = "id")
var id : Int,
@ColumnInfo(name = "name")
var name : String
) { ) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

@ -0,0 +1,64 @@
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,14 +2,13 @@ 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<MovieDetails>, val results : List<Movie>,
val totalResults : Int, val totalResults : Int,
val totalPages : Int val totalPages : Int

@ -1,21 +0,0 @@
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?
)
{}

@ -1,55 +0,0 @@
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
}
}

@ -1,34 +0,0 @@
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 : Long,
//spoken language
val status : String,
title : String,
voteAverage : Double,
voteCount : Int
) : Movie(posterPath, adult, overview, releaseDate, id, originalTitle, originalLanguage,
title, backdropPath, popularity, voteCount, voteAverage)
{
}

@ -1,62 +0,0 @@
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
}
}

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

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

@ -0,0 +1,66 @@
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
class MovieRepository() {
fun getPopularMovies(callback: (List<Movie>) -> Unit ) {
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)
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
})
}
fun getTrends(callback: (List<Movie>) -> Unit) {
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)
}
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
Log.d("Error failure", t.message.toString())
}
})
}
}

@ -0,0 +1,16 @@
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.utils.Constants.Companion.API_KEY
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class PopularRepository() {
suspend fun getPopular(): PopularDTO? {
throw NoSuchMethodError()
}
}

@ -1,92 +0,0 @@
package fr.iut.pm.movieapplication.repository.api
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
}
suspend fun search(query : String, Page : Int = 1) : List<MediaResult> = withContext(Dispatchers.IO) {
val listMediaResult : MutableList<MediaResult> = mutableListOf()
val response = RetrofitInstance.api.searchMedia(query, Page)
if(response.isSuccessful) {
val listMediaResultDTO = response.body()?.results
Log.d("Response",response.body().toString())
listMediaResultDTO?.forEach {
if(it?.mediaType == "movie" || it?.mediaType == "tv") {
val mediaResult = it?.let { it1 -> MediaResultMapper.mapToMediaResult(it1) }
mediaResult?.let { it1 -> listMediaResult.add(it1) }
mediaResult?.let { it1 -> Log.d("Movie", it1.title) }
}
}
}
else Log.d("ERROR FAILED", response.message())
listMediaResult
}
}

@ -1,104 +0,0 @@
package fr.iut.pm.movieapplication.repository.api
import android.util.Log
import fr.iut.pm.movieapplication.api.RetrofitInstance
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 MovieAPIRepository {
suspend fun getPopularMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
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
}
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
}
suspend fun getUpcomingMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
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()
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
}
}

@ -1,89 +0,0 @@
package fr.iut.pm.movieapplication.repository.api
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
}
}

@ -1,36 +0,0 @@
package fr.iut.pm.movieapplication.repository.local
import android.util.Log
import fr.iut.pm.movieapplication.data.dao.MovieDAO
import fr.iut.pm.movieapplication.data.mapper.MovieLocalMapper
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class MovieLocalRepository(private val movieDAO: MovieDAO) {
suspend fun insertMovie(movie : Movie) = withContext(Dispatchers.IO) {
movieDAO.insert(MovieLocalMapper.mapToMovieEntity(movie))
}
suspend fun insertMovieDetails(movieDetails : MovieDetails) = withContext(Dispatchers.IO) {
Log.i("MovieLocalRepository", "INSERT MOVIE DETAILS")
movieDAO.insertMovieDetailsWithGenres(MovieLocalMapper.mapToMovieDetailsWithGenres(movieDetails))
}
suspend fun getMovieDetailsById(id : Int) : MovieDetails {
return MovieLocalMapper.mapToMovieDetails(movieDAO.getMovieDetailsWithGenresById(id))
}
suspend fun getAllMovies() : List<Movie> = withContext(Dispatchers.IO) {
val listMovie : MutableList<Movie> = mutableListOf()
val listMoviesEntities = movieDAO.getAllMovies()
listMoviesEntities.forEach { movieEntity ->
listMovie.add(MovieLocalMapper.mapToMovie(movieEntity))
}
listMovie
}
}

@ -1,47 +1,72 @@
package fr.iut.pm.movieapplication.ui.activity package fr.iut.pm.movieapplication.ui.activity
import android.os.Build
import androidx.appcompat.app.AppCompatActivity 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.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.ui.fragments.* import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.api.config.GlobalImageConfig
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
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)
loadFragments(HomeSectionsFragment()) loadFragments(HomeSectionsFragment(this))
val navigationView = findViewById<BottomNavigationView>(R.id.navigation_view) val navigationView = findViewById<BottomNavigationView>(R.id.navigation_view)
navigationView.setOnItemSelectedListener { navigationView.setOnItemSelectedListener {
when(it.itemId) when(it.itemId)
{ {
R.id.home_page -> { R.id.home_page -> {
loadFragments(HomeSectionsFragment()) loadFragments(HomeSectionsFragment(this))
return@setOnItemSelectedListener true return@setOnItemSelectedListener true
} }
R.id.movies_page -> { R.id.movies_page -> {
loadFragments(MoviesFragment()) loadFragments(MoviesFragment(this))
return@setOnItemSelectedListener true return@setOnItemSelectedListener true
} }
R.id.series_page -> { R.id.series_page -> {
loadFragments(TvShowsFragment()) loadFragments(ShowsFragment(this))
return@setOnItemSelectedListener true
}
R.id.favorites_page -> {
loadFragments(FavoritesFragment())
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 {
@ -53,17 +78,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 {
if(!query.isNullOrEmpty()) { TODO("Not yet implemented")
loadFragments(SearchResultFragment.newInstance(query)) return false
}
return true;
}
//A améliorer
override fun onQueryTextChange(query: String?): Boolean {
if(!query.isNullOrEmpty()) {
loadFragments(SearchResultFragment.newInstance(query))
} }
return true
override fun onQueryTextChange(newText: String?): Boolean {
TODO("Not yet implemented")
return false
} }
}) })

@ -0,0 +1,12 @@
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
}
}

@ -0,0 +1,52 @@
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
}

@ -1,41 +0,0 @@
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.MediaSelection
import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection
import fr.iut.pm.movieapplication.utils.Constants
class MediaAdapter(private val listener : MediaSelection): 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: MediaSelection)
: 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))
}

@ -1,45 +0,0 @@
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))
}

@ -1,42 +0,0 @@
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.RecyclerView
import androidx.recyclerview.widget.ListAdapter
import coil.load
import fr.iut.pm.movieapplication.databinding.ItemSearchResultsBinding
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.ui.interfaces.MediaSelection
import fr.iut.pm.movieapplication.utils.Constants
class SearchResultAdapter(private val listener : MediaSelection)
: ListAdapter<MediaResult, SearchResultAdapter.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 : ItemSearchResultsBinding, listener: MediaSelection)
: 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(ItemSearchResultsBinding.inflate(LayoutInflater.from(parent.context)), listener)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position))
}

@ -1,48 +0,0 @@
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))
}

@ -1,47 +0,0 @@
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()
})
}
binding.registerMovieDetailsButton.setOnClickListener {
moviesDialogVM.register(binding.movieDetails!!)
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
}

@ -1,57 +0,0 @@
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.databinding.FragmentFavoritesCategoryBinding
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.FavoritesVM
class FavoritesFragment : Fragment(), MovieSelection {
private val favoritesVM by viewModels<FavoritesVM>()
private val movieAdapter = MovieAdapter(this);
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentFavoritesCategoryBinding.inflate(inflater)
binding.favoritesVM = favoritesVM
binding.lifecycleOwner = viewLifecycleOwner
with(binding.moviesItemRecyclerView) {
this.adapter = movieAdapter
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
favoritesVM.getFavoritesLiveData().observe(viewLifecycleOwner) {
movieAdapter.submitList(it)
}
}
override fun onMovieSelected(movieId: Int) {
val dialog = MovieDialog()
val args = Bundle()
args.putInt("movieId",movieId)
dialog.arguments = args
dialog.show(parentFragmentManager, "tag")
}
}

@ -1,73 +1,45 @@
package fr.iut.pm.movieapplication.ui.fragments package fr.iut.pm.movieapplication.ui.fragments
import android.os.Bundle import android.os.Bundle
import android.util.Log
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 androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView
import fr.iut.pm.movieapplication.databinding.FragmentHomeSectionsBinding import fr.iut.pm.movieapplication.R
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.MediaAdapter import fr.iut.pm.movieapplication.ui.adapter.HomeItemAdapter
import fr.iut.pm.movieapplication.ui.adapter.HomeItemDecoration import fr.iut.pm.movieapplication.ui.adapter.HomeItemDecoration
import fr.iut.pm.movieapplication.ui.interfaces.MediaSelection
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() : Fragment(), MediaSelection { class HomeSectionsFragment(
private val context : MainActivity
) : Fragment() {
private val homeSectionsVM by viewModels<HomeSectionsVM>() private lateinit var homeSectionsViewModel : HomeSectionsVM
private val trendsAdapter = MediaAdapter(this) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
private val popularMoviesAdapter = MediaAdapter(this) val view = inflater.inflate(R.layout.fragment_home_sections, container, false)
private val popularTvShowsAdapter = MediaAdapter(this)
override fun onCreateView( //get the trends RecyclerView
inflater: LayoutInflater, container: ViewGroup?, context.movieRepository.getTrends {
savedInstanceState: Bundle?) val homeTrendsRecyclerView = view?.findViewById<RecyclerView>(R.id.home_trends_recycler_view)
: View? { homeTrendsRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,it)
homeTrendsRecyclerView?.addItemDecoration(HomeItemDecoration())
val binding = FragmentHomeSectionsBinding.inflate(inflater)
binding.homeSectionsVM = homeSectionsVM
binding.lifecycleOwner = viewLifecycleOwner
with(binding.homeTrendsRecyclerView) {
adapter = trendsAdapter
addItemDecoration(HomeItemDecoration())
}
with(binding.homePopularMoviesRecyclerView) {
adapter = popularMoviesAdapter
addItemDecoration(HomeItemDecoration())
}
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) { //get the popularity RecyclerView
popularTvShowsAdapter.submitList(it) 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())
} }
override fun onMediaSelected(mediaId: Int) { //get the free RecyclerView
Log.d("ITEM SELECTED", mediaId.toString()) 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
} }
} }

@ -4,76 +4,39 @@ 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.databinding.FragmentMoviesBinding import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.ui.dialog.MovieDialog import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.ui.interfaces.MovieSelection 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.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(
) : Fragment(), MovieSelection { private val context : MainActivity
) : 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?
): View {
val binding = FragmentMoviesBinding.inflate(inflater)
binding.moviesVM = moviesVM
binding.lifecycleOwner = viewLifecycleOwner
with(binding.moviesItemRecyclerView) { context.movieRepository.getPopularMovies { listMovies ->
adapter = moviesAdapter 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())
} }
val adapter = ArrayAdapter.createFromResource( return view
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)
}
}
override fun onMovieSelected(movieId: Int) {
val dialog = MovieDialog()
val args = Bundle()
args.putInt("movieId",movieId)
dialog.arguments = args
dialog.show(parentFragmentManager, "tag")
} }
} }

@ -1,60 +0,0 @@
package fr.iut.pm.movieapplication.ui.fragments
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import fr.iut.pm.movieapplication.databinding.FragmentSearchResultsBinding
import fr.iut.pm.movieapplication.ui.adapter.MediaAdapter
import fr.iut.pm.movieapplication.ui.adapter.SearchResultAdapter
import fr.iut.pm.movieapplication.ui.interfaces.MediaSelection
import fr.iut.pm.movieapplication.ui.viewmodel.SearchResultVM
import fr.iut.pm.movieapplication.ui.viewmodel.SearchResultVMFactory
class SearchResultFragment : Fragment(), MediaSelection {
private val searchResultViewModel by viewModels<SearchResultVM> { SearchResultVMFactory(arguments?.getString("query")!!) }
private val searchResultAdapter = SearchResultAdapter(this)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentSearchResultsBinding.inflate(inflater, container, false)
binding.lifecycleOwner = viewLifecycleOwner
binding.searchResultsVM = searchResultViewModel
with(binding.searchResultsItemRecyclerView) {
this.adapter = searchResultAdapter
}
return binding.root;
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
searchResultViewModel.getSearchResultLiveData().observe(viewLifecycleOwner) {
searchResultAdapter.submitList(it)
}
}
override fun onMediaSelected(mediaId: Int) {
Log.d("ITEM SELECTED", mediaId.toString())
}
companion object {
fun newInstance(query: String): SearchResultFragment {
val fragment = SearchResultFragment()
val args = Bundle()
args.putString("query", query)
fragment.arguments = args
return fragment
}
}
}

@ -0,0 +1,15 @@
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)
}
}

@ -1,68 +0,0 @@
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)
}
}
}

@ -1,6 +0,0 @@
package fr.iut.pm.movieapplication.ui.interfaces
interface MediaSelection {
fun onMediaSelected(mediaId : Int)
}

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

@ -1,30 +0,0 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import fr.iut.pm.movieapplication.data.database.MovieDataBase
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.repository.local.MovieLocalRepository
import kotlinx.coroutines.launch
class FavoritesVM : ViewModel() {
private val repository = MovieLocalRepository(MovieDataBase.getInstance().movieDAO())
private var _favoritesLiveData : MutableLiveData<List<Movie>> = MutableLiveData()
fun getFavoritesLiveData() : MutableLiveData<List<Movie>> = _favoritesLiveData
init {
//with dispatchers.IO
viewModelScope.launch{
_favoritesLiveData.value = repository.getAllMovies()
}
}
}

@ -1,33 +1,4 @@
package fr.iut.pm.movieapplication.ui.viewmodel package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.MutableLiveData class HomeSectionsVM {
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.repository.api.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())
}
}
} }

@ -1,39 +0,0 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.*
import fr.iut.pm.movieapplication.data.database.MovieDataBase
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import fr.iut.pm.movieapplication.repository.api.MovieAPIRepository
import fr.iut.pm.movieapplication.repository.local.MovieLocalRepository
import kotlinx.coroutines.launch
class MoviesDialogVM(private val movieId : Int) : ViewModel() {
private val repository = MovieAPIRepository()
private val movieLocalRepository = MovieLocalRepository(
MovieDataBase.getInstance().movieDAO())
private var _movieDetailsLiveData : MutableLiveData<MovieDetails> = MutableLiveData()
fun getMovieDetailLiveData() : LiveData<MovieDetails> = _movieDetailsLiveData
init {
viewModelScope.launch {
if(movieId != 0) _movieDetailsLiveData.postValue(repository.getMovieDetails(movieId))
}
}
fun register(movieDetails: MovieDetails) = viewModelScope.launch {
movieLocalRepository.insertMovieDetails(movieDetails)
}
}
class MoviesDialogVMFactory(private val movieId : Int) : ViewModelProvider.Factory
{
override fun<T:ViewModel>create(modelClass:Class<T>) : T
{
return MoviesDialogVM(movieId) as T
}
}

@ -1,109 +1,37 @@
package fr.iut.pm.movieapplication.ui.viewmodel package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.* import androidx.lifecycle.*
import androidx.recyclerview.widget.GridLayoutManager import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import androidx.recyclerview.widget.RecyclerView import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.model.media.movie.Movie import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.repository.api.MovieAPIRepository import kotlinx.coroutines.CoroutineScope
import fr.iut.pm.movieapplication.utils.Constants.Companion.MAX_PAGE
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class MoviesVM() : ViewModel() { class MoviesVM(private val repository: MovieRepository) : ViewModel() {
/**
* The movie repository used to get our data
*/
private val repository = MovieAPIRepository()
/** private val _popularMovies = MutableLiveData<List<Movie>>()
* The MutableLiveData val popularMovies : LiveData<List<Movie>> = _popularMovies
*/
private var _moviesLiveData : MutableLiveData<List<Movie>> = MutableLiveData<List<Movie>>()
/** init {
* Getter of the LiveData //loadData()
*/
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
} }
}.join()
/**
* 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)
}
"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
} }
} }

@ -1,32 +0,0 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.*
import fr.iut.pm.movieapplication.model.media.MediaResult
import fr.iut.pm.movieapplication.repository.api.MediaRepository
import kotlinx.coroutines.launch
class SearchResultVM(private var query : String) : ViewModel() {
private val repository : MediaRepository = MediaRepository()
private var _queryLiveData : MutableLiveData<String> = MutableLiveData()
fun getQueryLiveData() : LiveData<String> = _queryLiveData
private var _searchResultLiveData : MutableLiveData<List<MediaResult>> = MutableLiveData()
fun getSearchResultLiveData() : LiveData<List<MediaResult>> = _searchResultLiveData
init {
_queryLiveData.value = query
viewModelScope.launch {
_searchResultLiveData.postValue(repository.search(query))
}
}
}
class SearchResultVMFactory(private var query : String) : ViewModelProvider.Factory
{
override fun<T:ViewModel>create(modelClass:Class<T>) : T
{
return SearchResultVM(query) as T
}
}

@ -1,43 +0,0 @@
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.api.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())
}
}
}
}

@ -0,0 +1,9 @@
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
}

@ -1,19 +1,10 @@
package fr.iut.pm.movieapplication.utils package fr.iut.pm.movieapplication.utils
import fr.iut.pm.movieapplication.BuildConfig
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 = BuildConfig.API_KEY const val API_KEY = "8f14a279249638d7f247d0d7298b21b4"
//VIEW PAGINATION
const val PAGE_SIZE = 20
const val MAX_PAGE = 1000
} }
} }

@ -0,0 +1,36 @@
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
)
}
}

@ -1,103 +0,0 @@
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
)
}
}

@ -1,48 +0,0 @@
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
)
}
}

@ -1,5 +0,0 @@
<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,38 +0,0 @@
<?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">
<data>
<import type="android.view.View" />
<variable
name="favoritesVM"
type="fr.iut.pm.movieapplication.ui.viewmodel.FavoritesVM" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<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"
tools:listitem="@layout/item_movie_category"
/>
</LinearLayout>
</layout>

@ -1,19 +1,7 @@
<?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">
<data> <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<import type="android.view.View" />
<variable
name="homeSectionsVM"
type="fr.iut.pm.movieapplication.ui.viewmodel.HomeSectionsVM" />
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/section_nested_scroll_view"> android:contentDescription="@string/section_nested_scroll_view">
@ -40,49 +28,42 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="250dp" android:layout_height="250dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
tools:listitem="@layout/item_search_results" />
<TextView <TextView
android:id="@+id/home_page_popularity_section_title" 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_popular_movies_section_title" android:text="@string/home_page_popularity_section_title"
android:layout_marginLeft="@dimen/default_margin" android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin" android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin" android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" /> style="@style/SectionTitleStyle" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_popular_movies_recycler_view" android:id="@+id/home_popularity_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="250dp" android:layout_height="250dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
tools:listitem="@layout/item_search_results" />
<TextView <TextView
android:id="@+id/home_page_free_section_title" 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_popular_tv_shows_section_title" android:text="@string/home_page_free_section_title"
android:layout_marginLeft="@dimen/default_margin" android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin" android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin" android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" /> style="@style/SectionTitleStyle" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_popular_tv_shows_recycler_view" android:id="@+id/home_free_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_marginBottom="@dimen/default_margin"
android:layout_height="250dp" android:layout_height="250dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
/> />
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</layout>

@ -1,54 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">
<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="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> xmlns:app="http://schemas.android.com/apk/res-auto">
<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: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 <androidx.recyclerview.widget.RecyclerView
android:id="@+id/movies_item_recycler_view" android:id="@+id/movies_item_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin" 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>

@ -1,31 +0,0 @@
<?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">
<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_search_results" />
</LinearLayout>
</layout>

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

@ -1,60 +0,0 @@
<?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>

@ -1,65 +0,0 @@
<?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">
<data>
<variable
name="mediaResult"
type="fr.iut.pm.movieapplication.model.media.MediaResult" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
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="@{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}"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</layout>

@ -1,67 +0,0 @@
<?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>

@ -0,0 +1,50 @@
<?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_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">
<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="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"
/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -1,93 +0,0 @@
<?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:text="@{movieDetails.overview.length() > 150 ? movieDetails.overview.substring(0, 150) + `...` : movieDetails.overview}"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@+id/register_movie_details_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/details_image"
app:layout_constraintTop_toBottomOf="@+id/details_image" />
<Button
android:id="@+id/register_movie_details_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>

@ -14,7 +14,7 @@
android:icon="@drawable/ic_tv_shows" android:icon="@drawable/ic_tv_shows"
android:title="@string/bottom_series_item" /> android:title="@string/bottom_series_item" />
<item <item
android:id="@+id/favorites_page" android:id="@+id/artist_page"
android:icon="@drawable/ic_baseline_star_border_24" android:icon="@drawable/ic_baseline_star_border_24"
android:title="@string/bottom_favorite_item" /> android:title="@string/bottom_artist_item" />
</menu> </menu>

@ -14,7 +14,7 @@
android:icon="@drawable/ic_tv_shows" android:icon="@drawable/ic_tv_shows"
android:title="@string/bottom_series_item" /> android:title="@string/bottom_series_item" />
<item <item
android:id="@+id/favorites_page" android:id="@+id/artist_page"
android:icon="@drawable/ic_baseline_star_border_24" android:icon="@drawable/ic_baseline_star_border_24"
android:title="@string/bottom_favorite_item" /> android:title="@string/bottom_artist_item" />
</menu> </menu>

@ -13,28 +13,4 @@
<item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
</style> </style>
<style name="DefaultTextStyle">
<item name="android:textColor">@color/white</item>
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">@font/source_sans_pro</item>
</style>
<style name="SubtitleTextStyle" parent="DefaultTextStyle">
<item name="android:textColor">@color/white</item>
<item name="android:textSize">20sp</item>
</style>
<style name="SectionTitleStyle" parent="DefaultTextStyle">
<item name="android:textSize">24sp</item>
</style>
<style name="TitleTextStyle" parent="SubtitleTextStyle">
<item name="android:textColor">@color/white</item>
<item name="android:textSize">24sp</item>
</style>
<style name="BottomMenuStyle" parent="Widget.Material3.BottomNavigationView">
<item name="android:textSize">16sp</item>
</style>
</resources> </resources>

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

@ -4,31 +4,14 @@
<string name="bottom_home_item">Accueil</string> <string name="bottom_home_item">Accueil</string>
<string name="bottom_movies_item">Films</string> <string name="bottom_movies_item">Films</string>
<string name="bottom_series_item">Séries</string> <string name="bottom_series_item">Séries</string>
<string name="bottom_favorite_item">Favoris</string> <string name="bottom_artist_item">Artistes</string>
<!-- Home page --> <!-- Home page -->
<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_popular_movies_section_title">Films populaires</string> <string name="home_page_popularity_section_title">Populaires</string>
<string name="home_page_popular_tv_shows_section_title">Séries populaires</string> <string name="home_page_free_section_title">Gratuits</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>

@ -7,7 +7,7 @@ plugins {
ext { ext {
activityVersion = '1.6.1' activityVersion = '1.6.1'
appCompatVersion = '1.6.1' appCompatVersion = '1.6.0'
constraintLayoutVersion = '2.1.4' constraintLayoutVersion = '2.1.4'
coreTestingVersion = '2.1.0' coreTestingVersion = '2.1.0'
coroutines = '1.6.4' coroutines = '1.6.4'

Loading…
Cancel
Save