diff --git a/CineCool/app/build.gradle b/CineCool/app/build.gradle index fb4072b..089e0ce 100644 --- a/CineCool/app/build.gradle +++ b/CineCool/app/build.gradle @@ -15,6 +15,8 @@ android { versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + multiDexEnabled true } buildTypes { @@ -31,28 +33,34 @@ android { jvmTarget = '1.8' } buildFeatures { + dataBinding true viewBinding true } } dependencies { - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1' - implementation 'androidx.navigation:navigation-ui-ktx:2.4.1' + implementation 'androidx.core:core-ktx:1.9.0' + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.8.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' + implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' implementation 'com.google.android.gms:play-services-location:21.0.1' + implementation 'androidx.core:core-ktx:1.9.0' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' implementation "com.squareup.okhttp3:okhttp:4.9.3" implementation "com.squareup.okhttp3:logging-interceptor:4.9.3" implementation "com.google.code.gson:gson:2.8.9" implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'androidx.fragment:fragment-ktx:1.5.6' + implementation 'androidx.multidex:multidex:2.0.1' + } \ No newline at end of file diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/ApiClient.kt b/CineCool/app/src/main/java/fr/iut/cinecool/ApiClient.kt index d1a3863..ff126be 100644 --- a/CineCool/app/src/main/java/fr/iut/cinecool/ApiClient.kt +++ b/CineCool/app/src/main/java/fr/iut/cinecool/ApiClient.kt @@ -1,21 +1,10 @@ package fr.iut.cinecool -import retrofit2.Call -import com.google.android.gms.common.api.Response -import com.google.gson.GsonBuilder -import fr.iut.cinecool.interfaces.ApiService -import fr.iut.cinecool.model.MovieResponse -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -import javax.security.auth.callback.Callback - class ApiClient { companion object { - private const val BASE_URL = "https://api.themoviedb.org/3/" + /*private const val BASE_URL = "https://api.themoviedb.org/3/" fun create(): ApiService { @@ -49,8 +38,8 @@ class ApiClient { val apiKey = "a97243d7813d31446f6c43284e6854d5" val call = movieApiService.getPopularMovies(apiKey) - // TO DO - /*call.enqueue(object : Callback { + + call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { // Code à exécuter lorsque la réponse est reçue val movies = response.body()?.results @@ -60,7 +49,7 @@ class ApiClient { override fun onFailure(call: Call, t: Throwable) { // Code à exécuter en cas d'échec de la requête } - })*/ - + }) + */ } } diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/DetailActivity.kt b/CineCool/app/src/main/java/fr/iut/cinecool/DetailActivity.kt index 03a55c0..2ecf9c7 100644 --- a/CineCool/app/src/main/java/fr/iut/cinecool/DetailActivity.kt +++ b/CineCool/app/src/main/java/fr/iut/cinecool/DetailActivity.kt @@ -5,49 +5,41 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import fr.iut.cinecool.interfaces.ITheMovieDbApiCallback +import fr.iut.cinecool.model.Movie +import fr.iut.cinecool.model.MovieAPI +import fr.iut.cinecool.services.TheMovieDbApiClient +import retrofit2.Call +import retrofit2.Response +import javax.security.auth.callback.Callback class DetailActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?){ + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.vu_detail) - // Récupération de la référence de l'ImageButton - val backButton: ImageButton =findViewById(R.id.backButton) + // Récupérer l'identifiant du film depuis les extras de l'intent + val movieId = intent.getIntExtra("movieId", -1) - // Ajout d'un listener pour gérer le clic sur le bouton - backButton.setOnClickListener{ - // Code à exécuter lorsque l'utilisateur clique sur le bouton de retour - // Par exemple, pour fermer l'activité en cours et revenir à l'activité précédente : - finish() - } - - // Récupération de la référence du TextView - val textView: TextView =findViewById(R.id.titreFilm) - - // Modification du texte affiché dans le TextView - textView.text="Titre de la page" - - // Récupération des références des ImageView - val imageView2: ImageView =findViewById(R.id.logo) - val imageView3:ImageView=findViewById(R.id.afficheFilm) - - // Chargement des images à partir de fichiers ou d'Internet, par exemple : - /*Glide.with(this) - .load(R.drawable.cinema) - .into(imageView2) - - Glide.with(this) - .load(R.drawable.imitation_game) - .into(imageView3)*/ - - // Récupération de la référence du TextView 2 - val textView2:TextView=findViewById(R.id.description) - - // Modification du texte affiché dans le TextView 2 - textView2.text="Contenu de la page" + // Appeler la méthode pour charger les détails du film + // loadMovieDetails(movieId) } - + /*private fun loadMovieDetails(movieId: Int) { + val apiClient = TheMovieDbApiClient(getString(R.string.tmdb_api_key)) + apiClient.getMovieDetails(movieId, object : ITheMovieDbApiCallback { + override fun onSuccess(result: MovieAPI) { + // Afficher les détails du film dans la vue + titreFilm.text = result.title + description.text = result.overview + Picasso.get().load("https://image.tmdb.org/t/p/w500/${result.posterPath}").into(afficheFilm) + } + + override fun onError(error: Throwable) { + // Gérer l'erreur de récupération des détails du film + } + }) + }*/ } \ No newline at end of file diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/MovieDetailFragment.kt b/CineCool/app/src/main/java/fr/iut/cinecool/MovieDetailFragment.kt new file mode 100644 index 0000000..98df4ed --- /dev/null +++ b/CineCool/app/src/main/java/fr/iut/cinecool/MovieDetailFragment.kt @@ -0,0 +1,75 @@ +package fr.iut.cinecool + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class MovieDetailFragment : Fragment() { +/* + private lateinit var binding: FragmentMovieDetailBinding + + override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?, savedInstanceState: Bundle?): View + { + binding = FragmentMovieDetailBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) + { + super.onViewCreated(view, savedInstanceState) + + val movieId = arguments?.getInt(ARG_MOVIE_ID) + + // Appel de l'API pour récupérer les détails du film + CoroutineScope(Dispatchers.IO).launch { + val response = movieId?.let { MovieApiService().getMovieDetails(it) } + withContext(Dispatchers.Main) + { + if (response != null) { + if (response.isSuccessful) + { + val movie = response.body() + if (movie != null) + { + // Affichage des détails du film dans l'interface utilisateur + binding.titreFilm.text = movie.title + binding.description.text = movie.overview + binding.afficheFilm.load("https://image.tmdb.org/t/p/w500${movie.posterPath}") // Utilisation de la librairie Coil pour charger l'image + } + } else + { + Toast.makeText( + requireContext(), + getString(R.string.error_loading_movie), + Toast.LENGTH_SHORT + ).show() + } + } + } + } + // Ajout d'un écouteur de clic pour le bouton de retour + binding.backButton.setOnClickListener { + requireActivity().onBackPressed() + } + } + + companion object { + private const val ARG_MOVIE_ID = "movie_id" + + fun newInstance(movieId: Int): MovieDetailFragment { + val args = Bundle().apply { + putInt(ARG_MOVIE_ID, movieId) + } + return MovieDetailFragment().apply { + arguments = args + } + } + }*/ +} \ No newline at end of file diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/RetrofitClient.kt b/CineCool/app/src/main/java/fr/iut/cinecool/RetrofitClient.kt new file mode 100644 index 0000000..66d1a23 --- /dev/null +++ b/CineCool/app/src/main/java/fr/iut/cinecool/RetrofitClient.kt @@ -0,0 +1,25 @@ +package fr.iut.cinecool + +import fr.iut.cinecool.services.TheMovieDbApiClient +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitClient { + private const val BASE_URL = "https://api.themoviedb.org/3/" + + private val okHttpClient = OkHttpClient.Builder() + .connectTimeout(60, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(60, TimeUnit.SECONDS) + .build() + + private val retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .client(okHttpClient) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + val api: TheMovieDbApiClient = retrofit.create(TheMovieDbApiClient::class.java) +} diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ApiService.kt b/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ApiService.kt deleted file mode 100644 index b1fec6d..0000000 --- a/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ApiService.kt +++ /dev/null @@ -1,15 +0,0 @@ -package fr.iut.cinecool.interfaces - -import fr.iut.cinecool.model.Movie -import fr.iut.cinecool.model.MovieResponse -import retrofit2.Call -import retrofit2.http.GET -import retrofit2.http.Query - -interface ApiService { - - @GET("movie/popular") - fun getPopularMovies(@Query("api_key") apiKey: String): Call - - -} diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ITheMovieDbApiCallback.kt b/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ITheMovieDbApiCallback.kt new file mode 100644 index 0000000..4184600 --- /dev/null +++ b/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ITheMovieDbApiCallback.kt @@ -0,0 +1,6 @@ +package fr.iut.cinecool.interfaces + +interface ITheMovieDbApiCallback { + fun onSuccess(result: T) + fun onError(error: Throwable) +} diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ITheMovieDbService.kt b/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ITheMovieDbService.kt new file mode 100644 index 0000000..5ab8bbd --- /dev/null +++ b/CineCool/app/src/main/java/fr/iut/cinecool/interfaces/ITheMovieDbService.kt @@ -0,0 +1,12 @@ +package fr.iut.cinecool.interfaces + +import fr.iut.cinecool.model.Movie +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query + +interface ITheMovieDbService { + @GET("movie/{movieId}") + fun getMovieDetails(@Path("movieId") movieId: Int, @Query("api_key") apiKey: String): Call +} diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/model/MovieResponse.kt b/CineCool/app/src/main/java/fr/iut/cinecool/model/MovieResponse.kt deleted file mode 100644 index 07e1712..0000000 --- a/CineCool/app/src/main/java/fr/iut/cinecool/model/MovieResponse.kt +++ /dev/null @@ -1,7 +0,0 @@ -package fr.iut.cinecool.model -import com.google.gson.annotations.SerializedName - -// MovieResponse pour stocker la réponse JSON de l'API : -data class MovieResponse( - @SerializedName("results") val movies: List -) diff --git a/CineCool/app/src/main/java/fr/iut/cinecool/services/TheMovieDbApiClient.kt b/CineCool/app/src/main/java/fr/iut/cinecool/services/TheMovieDbApiClient.kt new file mode 100644 index 0000000..a6a474d --- /dev/null +++ b/CineCool/app/src/main/java/fr/iut/cinecool/services/TheMovieDbApiClient.kt @@ -0,0 +1,57 @@ +package fr.iut.cinecool.services + +import fr.iut.cinecool.interfaces.ITheMovieDbApiCallback +import fr.iut.cinecool.interfaces.ITheMovieDbService +import fr.iut.cinecool.model.MovieAPI +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class TheMovieDbApiClient(apiKey: String) { + private val service: ITheMovieDbService = Retrofit.Builder() + .baseUrl("https://api.themoviedb.org/3/") + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(ITheMovieDbService::class.java) + + private val apiKeyInterceptor = Interceptor { chain -> + val url = chain.request().url.newBuilder() + .addQueryParameter("tmdb_api_key", apiKey) + .build() + + val request = chain.request().newBuilder() + .url(url) + .build() + + chain.proceed(request) + } + + private val client: OkHttpClient = OkHttpClient.Builder() + .addInterceptor(apiKeyInterceptor) + .build() + + /*fun getMovieDetails(movieId: Int, callback: ITheMovieDbApiCallback) { + service.getMovieDetails(movieId).enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + if (response.isSuccessful) { + val result = response.body() + if (result != null) { + callback.onSuccess(result) + } else { + callback.onError(Throwable("Empty response body")) + } + } else { + callback.onError(Throwable("Error ${response.code()}: ${response.message()}")) + } + } + + override fun onFailure(call: Call, t: Throwable) { + callback.onError(t) + } + }) + }*/ +} diff --git a/CineCool/app/src/main/res/drawable/Back_arrow.png b/CineCool/app/src/main/res/drawable/back_arrow.png similarity index 100% rename from CineCool/app/src/main/res/drawable/Back_arrow.png rename to CineCool/app/src/main/res/drawable/back_arrow.png diff --git a/CineCool/app/src/main/res/drawable/Filtrage.png b/CineCool/app/src/main/res/drawable/filtrage.png similarity index 100% rename from CineCool/app/src/main/res/drawable/Filtrage.png rename to CineCool/app/src/main/res/drawable/filtrage.png diff --git a/CineCool/app/src/main/res/layout/vu_detail.xml b/CineCool/app/src/main/res/layout/vu_detail.xml index 44d94ec..e913444 100644 --- a/CineCool/app/src/main/res/layout/vu_detail.xml +++ b/CineCool/app/src/main/res/layout/vu_detail.xml @@ -14,7 +14,7 @@ android:layout_marginTop="10dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/Back_arrow" /> + app:srcCompat="@drawable/back_arrow" /> Hello first fragment Hello second fragment. Arg: %1$s + erreur de chargement des films + a97243d7813d31446f6c43284e6854d5 \ No newline at end of file diff --git a/CineCool/build.gradle b/CineCool/build.gradle index ab2b874..3e76939 100644 --- a/CineCool/build.gradle +++ b/CineCool/build.gradle @@ -2,5 +2,5 @@ plugins { id 'com.android.application' version '7.4.2' apply false id 'com.android.library' version '7.4.2' apply false - id 'org.jetbrains.kotlin.android' version '1.8.0' apply false + id 'org.jetbrains.kotlin.android' version '1.8.20-RC2' apply false } \ No newline at end of file