Merge branch 'features/retrofit/1' into develop

develop
Jordan ARTZET 2 years ago
commit 96e30ea2a5

@ -42,6 +42,8 @@ dependencies {
implementation "androidx.appcompat:appcompat:$rootProject.appCompatVersion" implementation "androidx.appcompat:appcompat:$rootProject.appCompatVersion"
implementation "androidx.activity:activity-ktx:$rootProject.activityVersion" implementation "androidx.activity:activity-ktx:$rootProject.activityVersion"
implementation 'androidx.core:core-ktx:1.9.0'
implementation "androidx.fragment:fragment-ktx:1.5.5"
// Room components // Room components
implementation "androidx.room:room-ktx:$rootProject.roomVersion" implementation "androidx.room:room-ktx:$rootProject.roomVersion"
@ -58,10 +60,25 @@ dependencies {
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines" api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines"
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines" api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"
//Coil
implementation "io.coil-kt:coil:1.1.1"
// UI // UI
implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion" implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion"
implementation "com.google.android.material:material:$rootProject.materialVersion" implementation "com.google.android.material:material:$rootProject.materialVersion"
// Moshi
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
//GSON
//implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with Scalar Converter
//implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
// Retrofit with Moshi Converter
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
// Testing // Testing
testImplementation "junit:junit:$rootProject.junitVersion" testImplementation "junit:junit:$rootProject.junitVersion"
androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion" androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion"

@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"

@ -0,0 +1,22 @@
package fr.iut.pm.movieapplication.api
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.api.dtos.PopularDTO
import fr.iut.pm.movieapplication.utils.Constants.Companion.API_KEY
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query
interface MovieApplicationAPI {
@GET("movie/popular")
fun getPopularMovies(@Query("api_key") apiKey : String = API_KEY) : Call<PopularDTO>
@GET("trending/{media_type}/{time_window}")
fun getTrending(@Path("media_type") mediaType : String = "all", @Path("time_window") timeWindow : String = "day", @Query("api_key") apiKey: String = API_KEY ) : Call<PopularDTO>
@GET("movie/{movie_id")
fun getMovieDetails(@Path("movie_id") movieId : Int, @Query("api_key") apiKey: String = API_KEY) : Call<MovieResultDTO>
}

@ -0,0 +1,23 @@
package fr.iut.pm.movieapplication.api
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import fr.iut.pm.movieapplication.utils.Constants.Companion.BASE_URL
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
object RetrofitInstance {
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
val api: MovieApplicationAPI = retrofit.create(MovieApplicationAPI::class.java)
}

@ -0,0 +1,51 @@
package fr.iut.pm.movieapplication.api.config
import android.util.Log
import com.squareup.moshi.Json
object GlobalImageConfig {
@Json(name = "base_url")
private var _baseUrl : String = ""
val baseUrl : String = _baseUrl
@Json(name = "secure_base_url")
private var _secureBaseUrl : String = ""
val secureBaseUrl = _secureBaseUrl
@Json(name = "backdrop_sizes")
private var backdropSizes : List<String> = listOf()
@Json(name = "logo_sizes")
private var logoSizes : List<String> = listOf()
@Json(name = "poster_sizes")
private var posterSizes : List<String> = listOf()
@Json(name = "profile_sizes")
private var profilSizes : List<String> = listOf()
@Json(name = "still_sizes")
private var stillSizes : List<String> = listOf()
fun updateConfig(config: ImageConfig) {
Log.d("BASE URL IMAGE", baseUrl)
_baseUrl = config.baseUrl
_secureBaseUrl = config.secureBaseUrl
posterSizes = config.posterSizes
backdropSizes = config.backdropSizes
}
}
data class ImageConfig(
@Json(name = "images.base_url")
val baseUrl : String,
@Json(name = "images.secure_base_url")
val secureBaseUrl : String,
@Json(name = "images.backdrop_sizes")
val backdropSizes : List<String>,
@Json(name = "images.logo_sizes")
val logoSizes : List<String>,
@Json(name = "images.poster_sizes")
val posterSizes : List<String>,
@Json(name = "images.profile_sizes")
val profileSizes : List<String>,
@Json(name = "images.still_sizes")
val stillSizes : List<String>
)

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

@ -0,0 +1,40 @@
package fr.iut.pm.movieapplication.api.dtos
import com.squareup.moshi.Json
class MovieResultDTO(
@Json(name = "poster_path")
val posterPath : String?,
@Json(name = "adult")
val adult : Boolean,
@Json(name = "overview")
val overview : String,
@Json(name = "release_date")
val releaseDate : String?,
@Json(name = "genre_ids")
val genreIds : List<Int>?,
@Json(name = "id")
val id : Int,
@Json(name = "original_title")
val originalTitle : String?,
@Json(name = "original_language")
val originalLanguage : String?,
@Json(name = "title")
val title : String?,
@Json(name = "backdrop_path")
val backdropPath : String?,
@Json(name = "popularity")
val popularity : Double?,
@Json(name = "vote_count")
val voteCount : Int?,
val video : Boolean?,
@Json(name = "vote_average")
val voteAverage : Double?
) {
}

@ -0,0 +1,18 @@
package fr.iut.pm.movieapplication.api.dtos
import com.squareup.moshi.Json
class PopularDTO(
@Json(name = "page")
val page : Int,
@Json(name = "results")
val results : List<MovieResultDTO>,
@Json(name = "total_results")
val totalResults : Int,
@Json(name = "total_pages")
val totalPages : Int
) {}

@ -1,6 +1,8 @@
package fr.iut.pm.movieapplication.data.dao package fr.iut.pm.movieapplication.data.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import fr.iut.pm.movieapplication.model.Movie import fr.iut.pm.movieapplication.model.Movie
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -11,5 +13,11 @@ interface MovieDAO {
@Query("SELECT * FROM movies_table ORDER BY original_title ASC") @Query("SELECT * FROM movies_table ORDER BY original_title ASC")
fun getMovieByAlphabetizeMovie() : Flow<List<Movie>> fun getMovieByAlphabetizeMovie() : Flow<List<Movie>>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(movie : Movie)
@Query("DELETE FROM movies_table")
suspend fun deleteAll()
} }

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

@ -1,57 +1,50 @@
package fr.iut.pm.movieapplication.model package fr.iut.pm.movieapplication.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Embedded
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import androidx.room.Relation
import java.util.Date
@Entity(tableName = "movies_table") @Entity(tableName = "movies_table")
data class Movie( data class Movie(
@ColumnInfo(name = "adult") @ColumnInfo(name = "adult")
val adult: Boolean, val adult: Boolean,
@ColumnInfo(name = "budget") @ColumnInfo(name = "budget")
val budget: Int, val budget: Int?,
@ColumnInfo(name = "genres") @ColumnInfo(name = "genres")
val genres: Array<Genre>, val genres: Array<Genre>?,
@ColumnInfo(name = "homepage") @ColumnInfo(name = "homepage")
val homePage: String?, val homePage: String?,
@PrimaryKey @PrimaryKey
@ColumnInfo(name = "id") @ColumnInfo(name = "id")
val movieId: Int, val movieId: Int,
@ColumnInfo(name = "original_language") @ColumnInfo(name = "original_language")
val originalLanguage: String, val originalLanguage: String?,
@ColumnInfo(name = "original_title") @ColumnInfo(name = "original_title")
val originalTitle: String, val originalTitle: String?,
val overview: String?, val overview: String?,
val popularity: Int, val popularity: Double?,
@ColumnInfo(name = "poster_path") @ColumnInfo(name = "poster_path")
val posterPath : String?, val posterPath: String?,
@Embedded val productionCompanies: Array<ProductionCompany>?,
val productionCompanies: Array<ProductionCompany>,
@Relation(
parentColumn = "movieId",
entityColumn = ""
)
@ColumnInfo(name = "production_countries") @ColumnInfo(name = "production_countries")
val productionCountries: Array<ProductionCountry>, val productionCountries: Array<ProductionCountry>?,
@ColumnInfo(name = "release_date") @ColumnInfo(name = "release_date")
val releaseDate: Date, val releaseDate: String?,
val revenue: Int, val revenue: Int?,
val runtime: Int?, val runtime: Int?,
//var spokenLanguages : Array<SpokenLanguage>, //var spokenLanguages : Array<SpokenLanguage>,
val status: String, val status: String?,
@ColumnInfo(name = "tag_line") @ColumnInfo(name = "tag_line")
val tagLine: String?, val tagLine: String?,
val title: String, val title: String?,
@ColumnInfo(name = "vote_average") @ColumnInfo(name = "vote_average")
val voteAverage: Number, val voteAverage: Double?,
@ColumnInfo(name = "vote_count") @ColumnInfo(name = "vote_count")
val voteCount : Int val voteCount: Int?,
val backdropPath: String?
) { ) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

@ -0,0 +1,17 @@
package fr.iut.pm.movieapplication.model
import androidx.room.ColumnInfo
import androidx.room.Embedded
data class Popular(
@ColumnInfo("page")
val page : Int,
@ColumnInfo("results")
@Embedded
val results : List<Movie>,
val totalResults : Int,
val totalPages : Int
){
}

@ -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()
}
}

@ -3,14 +3,29 @@ package fr.iut.pm.movieapplication.ui.activity
import android.os.Build 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.View import android.view.View
import android.widget.SearchView.OnQueryTextListener
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import fr.iut.pm.movieapplication.R import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.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.HomeSectionsFragment
import fr.iut.pm.movieapplication.ui.fragments.MoviesFragment import fr.iut.pm.movieapplication.ui.fragments.MoviesFragment
import fr.iut.pm.movieapplication.ui.fragments.ShowsFragment
import fr.iut.pm.movieapplication.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)
@ -30,6 +45,11 @@ class MainActivity : AppCompatActivity() {
loadFragments(MoviesFragment(this)) loadFragments(MoviesFragment(this))
return@setOnItemSelectedListener true return@setOnItemSelectedListener true
} }
R.id.series_page -> {
loadFragments(ShowsFragment(this))
return@setOnItemSelectedListener true
}
else -> false else -> false
} }
} }
@ -47,11 +67,29 @@ class MainActivity : AppCompatActivity() {
// status bar is hidden, so hide that too if necessary. // status bar is hidden, so hide that too if necessary.
actionBar?.hide() actionBar?.hide()
} }
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.app_menu, menu)
val searchItem = menu?.findItem(R.id.search_bar)
val searchView : SearchView = searchItem?.actionView as SearchView
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
}
})
return true
} }
private fun loadFragments(fragment: Fragment) { private fun loadFragments(fragment: Fragment) {

@ -1,19 +1,30 @@
package fr.iut.pm.movieapplication.ui.adapter package fr.iut.pm.movieapplication.ui.adapter
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 android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import androidx.core.net.toUri
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load
import fr.iut.pm.movieapplication.R 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.ui.activity.MainActivity
import fr.iut.pm.movieapplication.utils.Constants.Companion.IMG_URL
class HomeItemAdapter( class HomeItemAdapter(
private val context : MainActivity, private val context: MainActivity,
private val layoutId : Int private val layoutId: Int,
private val list: List<Movie>
) : RecyclerView.Adapter<HomeItemAdapter.ViewHolder>() { ) : RecyclerView.Adapter<HomeItemAdapter.ViewHolder>() {
class ViewHolder(view : View) : RecyclerView.ViewHolder(view) { 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 { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -23,7 +34,19 @@ class HomeItemAdapter(
return ViewHolder(view) return ViewHolder(view)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) {} 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()
override fun getItemCount(): Int = 5 }
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
} }

@ -7,30 +7,38 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import fr.iut.pm.movieapplication.R 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.HomeItemAdapter 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.viewmodel.HomeSectionsVM
class HomeSectionsFragment( class HomeSectionsFragment(
private val context : MainActivity private val context : MainActivity
) : Fragment() { ) : Fragment() {
private lateinit var homeSectionsViewModel : HomeSectionsVM
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_home_sections, container, false) val view = inflater.inflate(R.layout.fragment_home_sections, container, false)
//get the trends RecyclerView //get the trends RecyclerView
context.movieRepository.getTrends {
val homeTrendsRecyclerView = view?.findViewById<RecyclerView>(R.id.home_trends_recycler_view) val homeTrendsRecyclerView = view?.findViewById<RecyclerView>(R.id.home_trends_recycler_view)
homeTrendsRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page) homeTrendsRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,it)
homeTrendsRecyclerView?.addItemDecoration(HomeItemDecoration()) homeTrendsRecyclerView?.addItemDecoration(HomeItemDecoration())
}
//get the popularity RecyclerView //get the popularity RecyclerView
context.movieRepository.getPopularMovies {
val homePopularityRecyclerView = view?.findViewById<RecyclerView>(R.id.home_popularity_recycler_view) val homePopularityRecyclerView = view?.findViewById<RecyclerView>(R.id.home_popularity_recycler_view)
homePopularityRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page) homePopularityRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,it)
homePopularityRecyclerView?.addItemDecoration(HomeItemDecoration()) homePopularityRecyclerView?.addItemDecoration(HomeItemDecoration())
}
//get the free RecyclerView //get the free RecyclerView
val homeFreeRecyclerView = view?.findViewById<RecyclerView>(R.id.home_free_recycler_view) val homeFreeRecyclerView = view?.findViewById<RecyclerView>(R.id.home_free_recycler_view)
homeFreeRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page) homeFreeRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_horizontal_home_page,ArrayList())
homeFreeRecyclerView?.addItemDecoration(HomeItemDecoration()) homeFreeRecyclerView?.addItemDecoration(HomeItemDecoration())
return view return view
} }

@ -5,28 +5,37 @@ 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.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import fr.iut.pm.movieapplication.R import fr.iut.pm.movieapplication.R
import fr.iut.pm.movieapplication.api.RetrofitInstance
import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.repository.MovieRepository
import fr.iut.pm.movieapplication.ui.activity.MainActivity import fr.iut.pm.movieapplication.ui.activity.MainActivity
import fr.iut.pm.movieapplication.ui.adapter.CategoryItemDecoration import fr.iut.pm.movieapplication.ui.adapter.CategoryItemDecoration
import fr.iut.pm.movieapplication.ui.adapter.HomeItemAdapter import fr.iut.pm.movieapplication.ui.adapter.HomeItemAdapter
import fr.iut.pm.movieapplication.ui.adapter.HomeItemDecoration import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVM
import fr.iut.pm.movieapplication.ui.viewmodel.MoviesVMFactory
import fr.iut.pm.movieapplication.ui.viewmodel.viewModelFactory
import kotlinx.coroutines.launch
class MoviesFragment( class MoviesFragment(
private val context : MainActivity private val context : MainActivity
) : Fragment() { ) : Fragment() {
private val moviesVM: MoviesVM by viewModels{ MoviesVMFactory(repository = MovieRepository())}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_movies, container, false) val view = inflater.inflate(R.layout.fragment_movies, container, false)
//get the recycler view context.movieRepository.getPopularMovies { listMovies ->
val moviesRecyclerView = view?.findViewById<RecyclerView>(R.id.movies_item_recycler_view) val moviesRecyclerView = view?.findViewById<RecyclerView>(R.id.movies_item_recycler_view)
moviesRecyclerView?.adapter = HomeItemAdapter(context,R.layout.item_vertical_fragment) moviesRecyclerView?.adapter = HomeItemAdapter(context, R.layout.item_vertical_fragment, listMovies)
moviesRecyclerView?.layoutManager = GridLayoutManager(context, 2) moviesRecyclerView ?. layoutManager = GridLayoutManager (context, 2)
moviesRecyclerView?.addItemDecoration(CategoryItemDecoration()) moviesRecyclerView?.addItemDecoration(CategoryItemDecoration())
}
return view return view
} }

@ -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)
}
}

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

@ -0,0 +1,37 @@
package fr.iut.pm.movieapplication.ui.viewmodel
import androidx.lifecycle.*
import fr.iut.pm.movieapplication.api.dtos.MovieResultDTO
import fr.iut.pm.movieapplication.model.Movie
import fr.iut.pm.movieapplication.repository.MovieRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class MoviesVM(private val repository: MovieRepository) : ViewModel() {
private val _popularMovies = MutableLiveData<List<Movie>>()
val popularMovies : LiveData<List<Movie>> = _popularMovies
init {
//loadData()
}
suspend fun loadData() {
viewModelScope.launch {
repository.getPopularMovies { movies ->
_popularMovies.value = movies
}
}.join()
}
}
class MoviesVMFactory(
private val repository: MovieRepository
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return MoviesVM(repository) as T
}
}

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

@ -0,0 +1,10 @@
package fr.iut.pm.movieapplication.utils
class Constants {
companion object {
const val BASE_URL = "https://api.themoviedb.org/3/"
const val IMG_URL = "https://image.tmdb.org/t/p/w500"
const val API_KEY = "8f14a279249638d7f247d0d7298b21b4"
}
}

@ -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
)
}
}

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="@color/light_green"/>
<item android:state_checked="false" android:color="@color/light_grey"/>
</selector>

@ -11,7 +11,7 @@
android:id="@+id/fragment_container" android:id="@+id/fragment_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="75dp" android:layout_marginBottom="55dp"
app:layout_constraintBottom_toTopOf="@+id/navigation_view" app:layout_constraintBottom_toTopOf="@+id/navigation_view"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -20,10 +20,14 @@
<com.google.android.material.bottomnavigation.BottomNavigationView <com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navigation_view" android:id="@+id/navigation_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="75dp" android:layout_height="wrap_content"
android:background="@color/dark_blue"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:itemTextColor="@drawable/item_selector"
app:itemIconTint="@drawable/item_selector"
app:labelVisibilityMode="labeled"
app:menu="@menu/bottom_navigation_menu" /> app:menu="@menu/bottom_navigation_menu" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -17,7 +17,11 @@
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_trends_section_title" android:text="@string/home_page_trends_section_title"
android:textSize="20sp" /> android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle"
/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_trends_recycler_view" android:id="@+id/home_trends_recycler_view"
@ -31,7 +35,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/home_page_popularity_section_title" android:text="@string/home_page_popularity_section_title"
android:textSize="20sp" /> android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_popularity_recycler_view" android:id="@+id/home_popularity_recycler_view"
@ -45,7 +52,10 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/home_page_free_section_title" android:text="@string/home_page_free_section_title"
android:textSize="20sp" /> android:layout_marginLeft="@dimen/default_margin"
android:layout_marginStart="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
style="@style/SectionTitleStyle" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_free_recycler_view" android:id="@+id/home_free_recycler_view"

@ -8,6 +8,7 @@
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="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
/> />
</LinearLayout> </LinearLayout>

@ -4,19 +4,20 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="150dp" android:layout_width="150dp"
android:layout_height="match_parent" > android:layout_height="match_parent"
android:layout_marginTop="10dp">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:id="@+id/cardView" android:id="@+id/cardView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="200dp"
app:cardCornerRadius="15dp" app:cardCornerRadius="5dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<ImageView <ImageView
android:id="@+id/home_section_item_image" android:id="@+id/item_image"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/black" android:background="@color/black"
@ -26,21 +27,19 @@
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<TextView <TextView
android:id="@+id/home_section_item_name" android:id="@+id/item_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Nom du film"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cardView" /> app:layout_constraintTop_toBottomOf="@+id/cardView" />
<TextView <TextView
android:id="@+id/home_section_item_date" android:id="@+id/item_date"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="23 nov 2012"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/home_section_item_name" /> app:layout_constraintTop_toBottomOf="@+id/item_name" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

@ -2,7 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="175dp" android:layout_width="175dp"
android:layout_height="match_parent"> android:layout_height="wrap_content">
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -10,6 +10,7 @@
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginTop="@dimen/default_margin"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:cardCornerRadius="10dp"> app:cardCornerRadius="10dp">
@ -19,22 +20,24 @@
android:orientation="vertical"> android:orientation="vertical">
<ImageView <ImageView
android:id="@+id/item_image"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="210dp" android:layout_height="210dp"
android:scaleType="centerCrop"
android:background="@color/black" /> android:background="@color/black" />
<TextView <TextView
android:id="@+id/item_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="20dp"
android:text="Nom du film"
android:layout_gravity="center" android:layout_gravity="center"
android:textAlignment="center" android:textAlignment="center"
/> />
<TextView <TextView
android:id="@+id/item_date"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="20dp"
android:text="23 nov 2015"
android:layout_gravity="center" android:layout_gravity="center"
android:textAlignment="center" android:textAlignment="center"
/> />

@ -3,21 +3,18 @@
<item <item
android:id="@+id/home_page" android:id="@+id/home_page"
android:title="@string/bottom_home_item"
android:icon="@drawable/ic_home" android:icon="@drawable/ic_home"
/> android:title="@string/bottom_home_item" />
<item <item
android:id="@+id/movies_page" android:id="@+id/movies_page"
android:title="@string/bottom_movies_item"
android:icon="@drawable/ic_movie" android:icon="@drawable/ic_movie"
/> android:title="@string/bottom_movies_item" />
<item <item
android:id="@+id/series_page" android:id="@+id/series_page"
android:title="@string/bottom_series_item"
android:icon="@drawable/ic_tv_shows" android:icon="@drawable/ic_tv_shows"
/> android:title="@string/bottom_series_item" />
<item <item
android:id="@+id/artist_page" android:id="@+id/artist_page"
android:title="@string/bottom_artist_item" android:icon="@drawable/ic_baseline_star_border_24"
android:icon="@drawable/ic_baseline_star_border_24"/> android:title="@string/bottom_artist_item" />
</menu> </menu>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/search_bar"
android:title="Search"
app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="androidx.appcompat.widget.SearchView" />
</menu>

@ -3,21 +3,18 @@
<item <item
android:id="@+id/home_page" android:id="@+id/home_page"
android:title="@string/bottom_home_item"
android:icon="@drawable/ic_home" android:icon="@drawable/ic_home"
/> android:title="@string/bottom_home_item" />
<item <item
android:id="@+id/movies_page" android:id="@+id/movies_page"
android:title="@string/bottom_movies_item"
android:icon="@drawable/ic_movie" android:icon="@drawable/ic_movie"
/> android:title="@string/bottom_movies_item" />
<item <item
android:id="@+id/series_page" android:id="@+id/series_page"
android:title="@string/bottom_series_item"
android:icon="@drawable/ic_tv_shows" android:icon="@drawable/ic_tv_shows"
/> android:title="@string/bottom_series_item" />
<item <item
android:id="@+id/artist_page" android:id="@+id/artist_page"
android:title="@string/bottom_artist_item" android:icon="@drawable/ic_baseline_star_border_24"
android:icon="@drawable/ic_baseline_star_border_24"/> android:title="@string/bottom_artist_item" />
</menu> </menu>

@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.MovieApplication" parent="Theme.Material3.Dark.NoActionBar"> <style name="Theme.MovieApplication" parent="Theme.Material3.Dark">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/dark_blue</item> <item name="colorPrimary">@color/dark_blue</item>
<item name="colorPrimaryVariant">@color/black</item> <item name="colorPrimaryVariant">@color/black</item>

@ -8,7 +8,9 @@
<color name="black">#FF000000</color> <color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color> <color name="white">#FFFFFFFF</color>
<color name="light_grey">#e3e3e3</color>
<color name="dark_blue">#032541</color> <color name="dark_blue">#032541</color>
<color name="light_blue">#01B4E4</color> <color name="light_blue">#01B4E4</color>
<color name="lighter_green">#c0fecf</color>
<color name="light_green">#1ed5a9 </color> <color name="light_green">#1ed5a9 </color>
</resources> </resources>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="default_margin">20dp</dimen>
</resources>

@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.MovieApplication" parent="@style/Theme.Material3.Light"> <style name="Theme.MovieApplication" parent="Theme.Material3.Light">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/light_blue</item> <item name="colorPrimary">@color/light_blue</item>
<item name="colorPrimaryVariant">@color/dark_blue</item> <item name="colorPrimaryVariant">@color/dark_blue</item>
@ -13,4 +13,30 @@
<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>
<!-- Create the component for the default title -->
<style name="DefaultTextStyle">
<item name="android:textColor">@color/black</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/black</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/black</item>
<item name="android:textSize">24sp</item>
</style>
<style name="BottomMenuStyle" parent="Widget.Material3.BottomNavigationView">
<item name="android:textSize">16sp</item>
</style>
</resources> </resources>

@ -12,7 +12,7 @@ ext {
coreTestingVersion = '2.1.0' coreTestingVersion = '2.1.0'
coroutines = '1.6.4' coroutines = '1.6.4'
lifecycleVersion = '2.5.1' lifecycleVersion = '2.5.1'
materialVersion = '1.7.0' materialVersion = '1.8.0'
roomVersion = '2.5.0' roomVersion = '2.5.0'
kotlin_version = '1.7.20' kotlin_version = '1.7.20'
// testing // testing

Loading…
Cancel
Save