Appel API fonctionnel

workShana
Tony Fages 3 months ago
parent 22de81e373
commit 21b87e9206

@ -1,6 +1,7 @@
plugins { plugins {
id("com.android.application") id("com.android.application")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
kotlin("plugin.serialization") version "1.5.10"
} }
android { android {
@ -55,12 +56,19 @@ dependencies {
implementation ("com.squareup.retrofit2:retrofit:2.9.0") implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0") implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0")
implementation ("androidx.compose.runtime:runtime-livedata:1.0.0-alpha07")
implementation("androidx.core:core-ktx:1.10.1") implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.activity:activity-compose:1.7.0") implementation("androidx.activity:activity-compose:1.7.0")
implementation("androidx.navigation:navigation-compose:2.4.0-alpha08") implementation("androidx.navigation:navigation-compose:2.4.0-alpha08")
implementation ("com.squareup.okhttp3:logging-interceptor:4.9.1")
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
implementation ("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0")
implementation(platform("androidx.compose:compose-bom:2023.08.00")) implementation(platform("androidx.compose:compose-bom:2023.08.00"))
implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-graphics")

@ -12,7 +12,10 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.VeraxApplication" android:theme="@style/Theme.VeraxApplication"
tools:targetApi="31"> tools:targetApi="31"
android:networkSecurityConfig="@xml/network_security_config"
>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

@ -1,8 +1,13 @@
package com.example.veraxapplication package com.example.veraxapplication
import ArticlesViewModel
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.veraxapplication.articles.IArticlesDataManager import com.example.veraxapplication.articles.IArticlesDataManager
import com.example.veraxapplication.articles.StubArticles import com.example.veraxapplication.articles.StubArticles
import com.example.veraxapplication.navigation.VeraxNavHost import com.example.veraxapplication.navigation.VeraxNavHost
@ -14,31 +19,39 @@ import com.example.veraxapplication.ui.topBar.TopBarVerax
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
VeraxContent()
}
}
}
class MainActivity : ComponentActivity() { @Composable
fun VeraxContent() {
// un truc vite fait pour avoir un visi
// var article = listOf("Thinkerview", "thinkerview.jgp", "Thinkerview est une chaîne youtube d'interview-débat")
//var articles = listOf( Article("Thinkerview", "This is a descrition", Author = "IAmAGreatAuthor", Image = "https://www.gstatic.com/webp/gallery/1.jpg", LectureTime = "12", Content = listOf(Paragraph("This is a paragraph"), Paragraph("This is another paragraph"), Paragraph("This is a third paragraph"))), Article("Thinkerview", "This is a descrition", Author = "IAmAGreatAuthor", Image = "https://www.gstatic.com/webp/gallery/1.jpg", LectureTime = "12", Content = listOf(Paragraph("This is a paragraph"), Paragraph("This is another paragraph"), Paragraph("This is a third paragraph"))))
// Initialiser les données ou observer les données du ViewModel
var dataManager: IArticlesDataManager = StubArticles() var dataManager: IArticlesDataManager = StubArticles()
var articles = dataManager.getDerniersArticles(4) var articles = dataManager.getDerniersArticles(4)
//val reqresClient = IArticleRepository.create<IArticleAPI>()
// Observer les données du ViewModel
val articlesViewModel: ArticlesViewModel = viewModel()
// Observez les articles du ViewModel
val articlesApi by articlesViewModel.articles.observeAsState(initial = articles)
var theme = listOf("Economique", "Culture", "Politique", "Faits divers") var theme = listOf("Economique", "Culture", "Politique", "Faits divers")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
//TopBarVerax(theme = theme, articles = articlesApi) TopBarVerax(articles = articlesApi, theme = theme)
TopBarVerax(theme = theme, articles = articles)
VeraxNavHost()
}
}
}
VeraxNavHost()
}

@ -11,33 +11,37 @@ data class ArticleDTO (
val titre: String, val titre: String,
@SerializedName("description") @SerializedName("description")
val description: String, val description: String,
@SerializedName("imagePrincipale") @SerializedName("temps")
val imagePrincipale: String, val temps: String,
@SerializedName("date") @SerializedName("date")
val date: String, val date: String,
@SerializedName("auteur") @SerializedName("auteur")
val auteur: String, val auteur: String,
@SerializedName("contenu") @SerializedName("categorie")
val contenu: List<ContenuDTO>, val categorie: String,
@SerializedName("temps") @SerializedName("imagePrincipale")
val temps: String, val imagePrincipale: String,
@SerializedName("note") @SerializedName("note")
val note: String, val note: String,
) { ) {
fun toModel(): Article { fun toModel(): Article {
return Article( return Article(
id, id,
titre, titre,
description, description,
auteur, temps,
date, date,
auteur,
categorie,
imagePrincipale, imagePrincipale,
temps,
note, note,
) )
} }
} }
data class ContenuDTO ( data class ContenuDTO (
@SerializedName("id") @SerializedName("id")
val id: Int, val id: Int,

@ -1,5 +0,0 @@
package com.example.veraxapplication.modele.api
class ArticleRepository {
}

@ -0,0 +1,54 @@
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.veraxapplication.modele.api.IArticleService
import com.example.veraxapplication.modele.articles.Article
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object ArticleApiClient {
private const val BASE_URL = "http://181.214.189.133:9092/"
private val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val httpClient = OkHttpClient.Builder().apply {
addInterceptor(logging)
}.build()
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build()
}
class ArticlesViewModel : ViewModel() {
private val _articles = MutableLiveData<List<Article>>()
val articles: LiveData<List<Article>> = _articles
private val service = ArticleApiClient.retrofit.create(IArticleService::class.java)
init {
loadArticles()
}
fun loadArticles() {
viewModelScope.launch {
try {
val articlesDto = service.getArticles() // Pas besoin d'appeler .execute()
// Convertissez les DTO en modèles de données si nécessaire
_articles.value = articlesDto.map { it.toModel() }
} catch (e: Exception) {
Log.e("ArticlesViewModel", "Erreur lors du chargement des articles", e)
}
}
}
}

@ -1,10 +0,0 @@
package com.example.veraxapplication.modele.api
import com.example.veraxapplication.modele.articles.Article
import retrofit2.Call
import retrofit2.http.GET
interface IArticleAPI {
@GET("articles/")
fun getArticles(): Call<List<Article>>
}

@ -0,0 +1,8 @@
package com.example.veraxapplication.modele.api
import retrofit2.http.GET
interface IArticleService {
@GET("articles")
suspend fun getArticles(): List<ArticleDTO>
}

@ -1,6 +1,5 @@
package com.example.veraxapplication.modele.articles package com.example.veraxapplication.modele.articles
import com.example.veraxapplication.modele.api.ArticleDTO
import com.example.veraxapplication.modele.articles.contenus.Contenu import com.example.veraxapplication.modele.articles.contenus.Contenu
class Article( class Article(
@ -10,10 +9,11 @@ class Article(
val temps: String, val temps: String,
val date: String, val date: String,
val auteur: String, val auteur: String,
val categorie: String,
val note: String, val note: String,
val imagePrincipale: String val imagePrincipale: String
) { ) {
private val lContenus: MutableList<Contenu> = java.util.ArrayList() public val lContenus: MutableList<Contenu> = java.util.ArrayList()
init { init {
// Initialisation des contenus si nécessaire // Initialisation des contenus si nécessaire

@ -24,8 +24,9 @@ class StubArticles() : IArticlesDataManager {
"Thinkerview", "Thinkerview",
"Thinkerview est une chaîne passionnante chaîne youtube d'interview-débat.", "Thinkerview est une chaîne passionnante chaîne youtube d'interview-débat.",
"3", "3",
"date a revoir", "02/09/2024",
"Siwa", "Tony",
"Politique",
"12", "12",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219554341388816437/stub1.webp?ex=660bb97d&is=65f9447d&hm=3e1e8d3372ae897fa4e2aa1ec730d976d74b35fce96cb8d78d6f9239e2836564&" "https://cdn.discordapp.com/attachments/1150826798549049554/1219554341388816437/stub1.webp?ex=660bb97d&is=65f9447d&hm=3e1e8d3372ae897fa4e2aa1ec730d976d74b35fce96cb8d78d6f9239e2836564&"
) )
@ -38,8 +39,9 @@ class StubArticles() : IArticlesDataManager {
"Le réchauffement climatique : un mythe ?", "Le réchauffement climatique : un mythe ?",
"Revenons sur les différentes controverses à ce sujet.", "Revenons sur les différentes controverses à ce sujet.",
"7", "7",
"date a revoir", "02/09/2024",
"Siwa", "Tony",
"Politique",
"12", "12",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219555874339815454/stub2.webp?ex=660bbaea&is=65f945ea&hm=80aef945e8410b18395c716fdd19265608f7b1263731192d5c69f807fce9e944&" "https://cdn.discordapp.com/attachments/1150826798549049554/1219555874339815454/stub2.webp?ex=660bbaea&is=65f945ea&hm=80aef945e8410b18395c716fdd19265608f7b1263731192d5c69f807fce9e944&"
) )
@ -52,8 +54,9 @@ class StubArticles() : IArticlesDataManager {
"La terre plate : vraiment ?", "La terre plate : vraiment ?",
"Pour réfuter la fausse croyance que la Terre est plate, il est essentiel de s'appuyer sur des preuves scientifiques et des observations historiques. ", "Pour réfuter la fausse croyance que la Terre est plate, il est essentiel de s'appuyer sur des preuves scientifiques et des observations historiques. ",
"5", "5",
"date a revoir", "02/09/2024",
"Siwa", "Tony",
"Politique",
"12", "12",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219547864196317225/stub1.webp?ex=660bb374&is=65f93e74&hm=a9e5dd48faa3ae68c358309af8949c46dfd4dea9c4d6e3d845d707784e5341cf&" "https://cdn.discordapp.com/attachments/1150826798549049554/1219547864196317225/stub1.webp?ex=660bb374&is=65f93e74&hm=a9e5dd48faa3ae68c358309af8949c46dfd4dea9c4d6e3d845d707784e5341cf&"
@ -67,8 +70,9 @@ class StubArticles() : IArticlesDataManager {
"L'ia & humanité : quel avenir ? ", "L'ia & humanité : quel avenir ? ",
"Explorons les progrès remarquables dans le domaine de l'IA, les secteurs qu'elle révolutionne, et les implications éthiques majeures qu'elle soulève.", "Explorons les progrès remarquables dans le domaine de l'IA, les secteurs qu'elle révolutionne, et les implications éthiques majeures qu'elle soulève.",
"9", "9",
"date a revoir", "02/09/2024",
"Luthen", "Tony",
"Politique",
"12", "12",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219560686254817290/stub1.webp?ex=660bbf65&is=65f94a65&hm=021bd8c90c89347f31373468cc7a03ae15f1d3f9988a5b4325149c6df938d7bb&" "https://cdn.discordapp.com/attachments/1150826798549049554/1219560686254817290/stub1.webp?ex=660bbf65&is=65f94a65&hm=021bd8c90c89347f31373468cc7a03ae15f1d3f9988a5b4325149c6df938d7bb&"
) )

@ -1,5 +1,6 @@
package com.example.veraxapplication.ui.article package com.example.veraxapplication.ui.article
import android.util.Log
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -7,8 +8,10 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@ -28,6 +31,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter import coil.compose.rememberImagePainter
import com.example.veraxapplication.modele.articles.Article import com.example.veraxapplication.modele.articles.Article
import com.example.veraxapplication.modele.articles.contenus.Contenu
import com.example.veraxapplication.modele.articles.contenus.ContenuMedia import com.example.veraxapplication.modele.articles.contenus.ContenuMedia
import com.example.veraxapplication.modele.articles.contenus.ContenuParagraphe import com.example.veraxapplication.modele.articles.contenus.ContenuParagraphe
import com.example.veraxapplication.ui.theme.Salmon import com.example.veraxapplication.ui.theme.Salmon
@ -59,21 +63,14 @@ fun AffichageUnArticleInfo(e : Article){
Text(text = e.titre, fontFamily = FontFamily.Serif, fontSize = 30.sp) Text(text = e.titre, fontFamily = FontFamily.Serif, fontSize = 30.sp)
Box(modifier = Modifier DisplayHeader(author = e.auteur, description = e.description , lectureTime = e.temps)
.padding(15.dp)
.border(width = 1.dp, color = Color.Black, shape = RoundedCornerShape(10.dp)) var imageURl = e.note
.clip(RoundedCornerShape(10.dp)) Log.d("ImageLoad", "URL de l'image reçue de l'API : $imageURl")
.background(Salmon)
) {
Column (modifier = Modifier.padding(15.dp)) {
Text(text = "Auteur : "+e.auteur, fontSize = 17.sp)
Text(text = "Description : "+e.description, fontSize = 17.sp)
Text(text = "Temps de lecture : "+e.temps+" minutes", fontSize = 17.sp)
}
}
Image( Image(
painter = rememberImagePainter( painter = rememberImagePainter(
data = e.imagePrincipale data = imageURl
), ),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
contentDescription = null, contentDescription = null,
@ -107,91 +104,115 @@ fun AfficherArticle(e : Article){
Text(text = e.titre, fontFamily = FontFamily.Serif, fontSize = 30.sp) Text(text = e.titre, fontFamily = FontFamily.Serif, fontSize = 30.sp)
Box( // Affichage des informations de l'article
modifier = Modifier DisplayHeader(author = e.auteur, description = e.description, lectureTime = e.temps)
.padding(15.dp)
.border(width = 1.dp, color = Color.Black, shape = RoundedCornerShape(10.dp)) Text(text = "coucou")
.clip(RoundedCornerShape(10.dp))
.background(Salmon) /*
) {
Column(modifier = Modifier.padding(15.dp)) {
Text(text = "Auteur : "+e.auteur, fontSize = 17.sp)
Text(text = "Description : "+e.description, fontSize = 17.sp)
Text(text = "Temps de lecture : " + e.temps + " minutes", fontSize = 17.sp)
}
}
Image( Image(
painter = rememberImagePainter( painter = rememberImagePainter(data = e.imagePrincipale),
data = e.imagePrincipale contentScale = ContentScale.FillHeight,
),
contentScale = ContentScale.Crop,
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.size(350.dp) .size(350.dp)
.align(Alignment.CenterHorizontally)
.padding(5.dp, 35.dp) .padding(5.dp, 35.dp)
) .fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
)*/
Column (modifier = Modifier.align(Alignment.Start)){ // Affichage contenus
for (text in e.contenus){ //DisplayContenu(e.lContenus)
when(text){
is ContenuMedia -> {
Text(text = text.titre, fontSize = 20.sp, fontWeight = FontWeight.Bold)
when(text.typeContenu){
"image" -> {
Image(
painter = rememberImagePainter(
data = text.lien
),
contentScale = ContentScale.FillHeight ,
contentDescription = null,
modifier = Modifier
.size(350.dp)
.align(Alignment.CenterHorizontally)
.padding(5.dp, 35.dp)
)
}
"video" -> {
Text(text = "Ici une video ..."+ text.toString(), fontSize = 15.sp, textAlign = TextAlign.Start)
//faut je vois comment on fait pour inclure une video ...
}
}
}
}
@Composable
fun DisplayHeader(author: String, description: String, lectureTime: String) {
Box(
modifier = Modifier
.padding(15.dp)
.border(width = 1.dp, color = Color.Black, shape = RoundedCornerShape(10.dp))
.clip(RoundedCornerShape(10.dp))
.background(Salmon)
) {
Column(modifier = Modifier.padding(15.dp)) {
Text(text = "Auteur : "+ author, fontSize = 17.sp)
Text(text = "Description : "+ description, fontSize = 17.sp)
Text(text = "Temps de lecture : " + lectureTime + " minutes", fontSize = 17.sp)
}
}
}
@Composable
fun DisplayContenu(contenus: List<Contenu>) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
for (contenu in contenus) {
when (contenu) {
is ContenuMedia -> {
Text(text = contenu.titre, fontSize = 20.sp, fontWeight = FontWeight.Bold)
when (contenu.typeContenu) {
"image" -> {
Image(
painter = rememberImagePainter(data = contenu.lien),
contentScale = ContentScale.FillHeight,
contentDescription = null,
modifier = Modifier
.size(350.dp)
.padding(5.dp, 35.dp)
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally) // Centrer l'image
)
}
"video" -> {
// Pour l'instant, un simple texte pour les vidéos
Text(text = "Ici une vidéo ... ${contenu.lien}", fontSize = 15.sp, textAlign = TextAlign.Start)
// Implémentation pour vidéo à venir...
}
} }
is ContenuParagraphe ->{ }
Text(text = text.titre, fontSize = 20.sp, fontWeight = FontWeight.Bold) is ContenuParagraphe -> {
Text(text = text.toString(), fontSize = 16.sp, textAlign = TextAlign.Start) Text(text = contenu.titre, fontSize = 20.sp, fontWeight = FontWeight.Bold)
} Text(text = contenu.texte, fontSize = 16.sp, textAlign = TextAlign.Start)
} }
} }
} }
} }
} }
/* /*
@Composable @Composable
fun DisplayImage(image: String) { fun DisplayImage(image: String) {
Log.d("DisplayImage", "Chargement de l'image à partir de l'URL : $image") Image(
painter = rememberImagePainter(
data = image
),
contentScale = ContentScale.Crop,
contentDescription = null,
modifier = Modifier
.size(350.dp)
.align(Alignment.CenterHorizontally)
.padding(5.dp, 35.dp)
)
}
Log.d("DisplayImage", "Painter créé avec succès")
Box( @Composable
modifier = Modifier.fillMaxSize(), fun DisplayImage(image: String) {
contentAlignment = Alignment.Center Image(
) {
Image(
painter = rememberImagePainter( painter = rememberImagePainter(
data = image, data = e.imagePrincipale
builder = {
scale(Scale.FILL)
}
), ),
contentScale = ContentScale.Crop,
contentDescription = null, contentDescription = null,
modifier = Modifier.fillMaxSize(), modifier = Modifier
.size(350.dp)
.align(Alignment.CenterHorizontally)
.padding(5.dp, 35.dp)
) )
}
} }

@ -35,7 +35,6 @@ import androidx.compose.ui.unit.sp
import com.example.veraxapplication.R import com.example.veraxapplication.R
import com.example.veraxapplication.modele.articles.Article import com.example.veraxapplication.modele.articles.Article
import com.example.veraxapplication.ui.article.AffichageLesArticles import com.example.veraxapplication.ui.article.AffichageLesArticles
import com.example.veraxapplication.ui.article.AfficherArticle
import com.example.veraxapplication.ui.theme.Orange import com.example.veraxapplication.ui.theme.Orange
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">181.214.189.133</domain>
</domain-config>
</network-security-config>
Loading…
Cancel
Save