🚧 some requests implemented need to had coroutines now to improve perf

features/api/requests/2
Jordan ARTZET 2 years ago
parent 76130b9358
commit 797ce00665

@ -27,7 +27,7 @@ interface MovieApplicationAPI {
// Movie details
@GET("movie/{movie_id}")
fun getMovieDetails(@Path("movie_id") movieId : Int, @Query("api_key") apiKey: String = API_KEY) : Response<MovieDetailsDTO>
suspend fun getMovieDetails(@Path("movie_id") movieId : Int, @Query("api_key") apiKey: String = API_KEY) : Response<MovieDetailsDTO>
// TvShow

@ -20,10 +20,10 @@ open class MediaResultDTO(
// val genreIds : List<Int>,
val id : Int,
@Json(name = "original_title")
val originalTitle : String? = null,
val originalTitle : String = "",
@Json(name = "original_language")
val originalLanguage : String,
val title : String?,
val title : String = "",
@Json(name = "backdrop_path")
val backdropPath : String?,
val popularity : Double,

@ -6,11 +6,9 @@ open class MovieDTO (
@Json(name = "poster_path")
open val posterPath: String?,
open val adult: Boolean,
open val overview: String,
open val overview: String?,
@Json(name = "release_date")
open val releaseDate: String,
@Json(name = "genre_ids")
open val genreIds: List<Int>,
open val id: Int,
@Json(name = "original_title")
open val originalTitle: String,
@ -22,7 +20,6 @@ open class MovieDTO (
open val popularity: Double,
@Json(name = "vote_count")
open val voteCount: Int,
open val video : Boolean,
@Json(name = "vote_average")
open val voteAverage: Double

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

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

@ -2,18 +2,16 @@ 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 genreIds: List<Int>,
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 title: String,
open val backdropPath: String?,
open val popularity: Double,
open val voteCount: Int,
//open val video: Boolean?,
open val voteAverage: Double
)
{
@ -27,7 +25,6 @@ open class Movie(
if (adult != other.adult) return false
if (overview != other.overview) return false
if (releaseDate != other.releaseDate) return false
// if (genreIds != other.genreIds) return false
if (id != other.id) return false
if (originalTitle != other.originalTitle) return false
if (originalLanguage != other.originalLanguage) return false
@ -45,7 +42,6 @@ open class Movie(
result = 31 * result + adult.hashCode()
result = 31 * result + overview.hashCode()
result = 31 * result + releaseDate.hashCode()
// result = 31 * result + genreIds.hashCode()
result = 31 * result + id
result = 31 * result + originalTitle.hashCode()
result = 31 * result + originalLanguage.hashCode()

@ -1,50 +1,34 @@
package fr.iut.pm.movieapplication.model.media.movie
import fr.iut.pm.movieapplication.model.ProductionCompany
import fr.iut.pm.movieapplication.model.ProductionCountry
data class MovieDetails(
override val posterPath: String,
override val adult: Boolean,
override val overview: String,
override val releaseDate: String,
// override val genreIds : List<Int>,
override val id: Int,
override val originalTitle: String,
override val originalLanguage: String,
override val title: String?,
override val backdropPath: String?,
override val popularity: Double,
override val voteCount: Int,
// override val video: Boolean?,
override val voteAverage: Double,
val mediaType: String,
val budget: Int?,
val homePage: String?,
val productionCompanies: Array<ProductionCompany>?,
val productionCountries: Array<ProductionCountry>?,
val revenue: Int?,
val runtime: Int?,
val status: String?,
val tagLine: String?
) : Movie(posterPath,
adult,
overview,
releaseDate,
// genreIds,
id,
originalTitle,
originalLanguage,
title,
backdropPath,
popularity,
voteCount,
// video,
voteAverage) {
import fr.iut.pm.movieapplication.model.Genre
class MovieDetails(
adult : Boolean?,
backdropPath : String?,
val budget : Int,
val genres : List<Genre>,
val homepage : String?,
id : Int,
originalLanguage : String,
originalTitle : String,
overview : String?,
popularity : Double,
posterPath : String?,
//prod companies
//prod countries
releaseDate : String?,
val revenue : Int,
//spoken language
val status : String,
title : String,
voteAverage : Double,
voteCount : Int
) : Movie(posterPath, adult, overview, releaseDate, id, originalTitle, originalLanguage,
title, backdropPath, popularity, voteCount, voteAverage)
{
}

@ -2,12 +2,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.MovieDetailsDTO
import fr.iut.pm.movieapplication.model.media.movie.Movie
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
import fr.iut.pm.movieapplication.utils.MediaResultMapper
import fr.iut.pm.movieapplication.utils.MovieMapper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class MovieRepository {
suspend fun getPopularMovies(page : Int = 1) : List<Movie>
suspend fun getPopularMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
@ -18,14 +22,14 @@ class MovieRepository {
listMediaResultDTO?.forEach {
val movie = MediaResultMapper.mapToMovie(it)
listMovie.add(movie)
Log.d("Movie ", movie.title!!)
Log.d("Movie ", movie.title!! + " " + movie.id)
}
}
else Log.d("ERROR FAILED", response.message())
return listMovie
listMovie
}
suspend fun getNowPlayingMovies(page : Int = 1) : List<Movie>
suspend fun getNowPlayingMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
@ -40,10 +44,10 @@ class MovieRepository {
}
}
else Log.d("ERROR FAILED", response.message())
return listMovie
listMovie
}
suspend fun getUpcomingMovies(page : Int = 1) : List<Movie>
suspend fun getUpcomingMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
@ -58,10 +62,10 @@ class MovieRepository {
}
}
else Log.d("ERROR FAILED", response.message())
return listMovie
listMovie
}
suspend fun getTopRatedMovies(page : Int = 1) : List<Movie>
suspend fun getTopRatedMovies(page : Int = 1) : List<Movie> = withContext(Dispatchers.IO)
{
val listMovie : MutableList<Movie> = mutableListOf()
@ -76,7 +80,25 @@ class MovieRepository {
}
}
else Log.d("ERROR FAILED", response.message())
return listMovie
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
}

@ -65,12 +65,10 @@ class MainActivity : AppCompatActivity() {
searchView.setOnQueryTextListener( object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
TODO("Not yet implemented")
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
TODO("Not yet implemented")
return false
}

@ -9,22 +9,25 @@ 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.utils.Constants
class MovieAdapter() : ListAdapter<Movie, MovieAdapter.ViewHolder>(DiffUtilMovieCallback) {
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) :
class ViewHolder(private val binding : ItemMovieCategoryBinding, listener: MovieSelection) :
RecyclerView.ViewHolder(binding.root) {
val movie : Movie? get() = binding.movie
init {
itemView.setOnClickListener {}
itemView.setOnClickListener {
listener.onMovieSelected(movie?.id ?: 0)
}
}
fun bind(movie : Movie) {
@ -37,8 +40,11 @@ class MovieAdapter() : ListAdapter<Movie, MovieAdapter.ViewHolder>(DiffUtilMovie
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(ItemMovieCategoryBinding.inflate(LayoutInflater.from(parent.context)))
ViewHolder(ItemMovieCategoryBinding.inflate(LayoutInflater.from(parent.context, )), listener)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position))
interface MovieSelection {
fun onMovieSelected(movieId : Int)
}
}

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

@ -10,13 +10,15 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.databinding.FragmentMoviesBinding
import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter
import fr.iut.pm.movieapplication.ui.dialog.MovieDialog
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVM
class MoviesFragment(
) : Fragment() {
) : Fragment(), MovieAdapter.MovieSelection {
private val moviesVM by viewModels<MoviesVM>()
val moviesAdapter = MovieAdapter(this)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@ -25,6 +27,10 @@ class MoviesFragment(
binding.moviesVM = moviesVM
binding.lifecycleOwner = viewLifecycleOwner
with(binding.moviesItemRecyclerView) {
adapter = moviesAdapter
}
val adapter = ArrayAdapter.createFromResource(
requireContext(),
R.array.movie_filter,
@ -58,7 +64,15 @@ class MoviesFragment(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
moviesVM.getMoviesLiveData().observe(viewLifecycleOwner) {
moviesVM.moviesAdapter.submitList(it)
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")
}
}

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

@ -34,7 +34,7 @@ class MoviesVM() : ViewModel() {
/**
* The adapter of the RecyclerView (set on the RecyclerView in the view)
*/
val moviesAdapter = MovieAdapter()
val scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@ -66,6 +66,7 @@ class MoviesVM() : ViewModel() {
when(currentFilter) {
"Populaires" -> viewModelScope.launch {
_moviesLiveData.postValue(repository.getPopularMovies())
repository.getMovieDetails(315162)
}
"Du moment" -> viewModelScope.launch {
_moviesLiveData.postValue(repository.getNowPlayingMovies())

@ -1,8 +1,12 @@
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 {
@ -64,5 +68,36 @@ object MediaResultMapper {
)
}
fun mapToMovieDetails(movieDetailsDTO: MovieDetailsDTO?): MovieDetails? {
if(movieDetailsDTO == null) return null
return MovieDetails(
posterPath = movieDetailsDTO.posterPath,
adult = movieDetailsDTO.adult!!,
overview = movieDetailsDTO.overview ?: "",
releaseDate = movieDetailsDTO.releaseDate ?: "",
id = movieDetailsDTO.id,
originalTitle = movieDetailsDTO.originalTitle!!,
originalLanguage = movieDetailsDTO.originalLanguage,
title = movieDetailsDTO.title,
backdropPath = movieDetailsDTO.backdropPath,
popularity = movieDetailsDTO.popularity,
voteCount = movieDetailsDTO.voteCount,
voteAverage = movieDetailsDTO.voteAverage,
budget = movieDetailsDTO.budget,
genres = movieDetailsDTO.genres.map { mapGenreDTOToGenre(it) },
homepage = movieDetailsDTO.homepage,
revenue = movieDetailsDTO.revenue,
status = movieDetailsDTO.status
)
}
fun mapGenreDTOToGenre(genreDTO : GenreDTO) : Genre {
return Genre(
name = genreDTO.name,
id = genreDTO.id
)
}
}

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

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

@ -45,7 +45,6 @@
android:layout_marginBottom="@dimen/default_margin"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="3"
android:adapter="@{moviesVM.moviesAdapter}"
app:onScrollListener = "@{moviesVM.scrollListener}"
tools:listitem="@layout/item_movie_category"

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="movieDetails"
type="fr.iut.pm.movieapplication.model.media.movie.MovieDetails" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/default_margin">
<ImageView
android:id="@+id/close_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:src="@drawable/ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="Close" />
<ImageView
android:id="@+id/details_image"
android:layout_width="wrap_content"
android:layout_height="200dp"
android:layout_marginTop="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:autoText="true"
android:hint="The title of the movie"
android:text="@{movieDetails.title}"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#000"
app:layout_constraintEnd_toStartOf="@+id/close_item"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/overview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@{movieDetails.overview}"
android:textAlignment="center"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/details_image"
app:layout_constraintTop_toBottomOf="@+id/details_image" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Enregistrer"
android:gravity="center_horizontal|center_vertical"
app:layout_constraintBottom_toTopOf="@+id/button2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Voir détails"
android:gravity="center_horizontal|center_vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/button"
app:layout_constraintStart_toStartOf="@+id/button" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Loading…
Cancel
Save