correcction tp 5 01

This commit is contained in:
Chocolaterie
2025-01-24 14:42:14 +01:00
parent 99c3458b79
commit bbb278d7b2
8 changed files with 179 additions and 24 deletions

View File

@@ -58,4 +58,15 @@ dependencies {
debugImplementation(libs.androidx.ui.test.manifest)
implementation("io.coil-kt:coil-compose:2.7.0")
// Moshi
implementation("com.squareup.moshi:moshi:1.14.0")
implementation("com.squareup.moshi:moshi-kotlin:1.14.0")
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
// Ok HTTP
implementation("com.squareup.okhttp3:okhttp:4.9.0")
}

View File

@@ -0,0 +1,24 @@
package com.example.tpfilrouge.api
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
class RetrofitTools {
// Kotlin : companion object = tout ce qui est dedans est statics
companion object {
// La racine de l'api
val BASE_URL = "https://raw.githubusercontent.com/Chocolaterie/EniWebService/refs/heads/main/api/"
// L'utilitaire conversion JSON <=> Objet
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build();
// Retrofit
val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL).build();
}
}

View File

@@ -0,0 +1,15 @@
package com.example.demoandroid.demoapi
import com.example.tpfilrouge.api.RetrofitTools.Companion.retrofit
import com.example.tpfilrouge.article.Article
import retrofit2.http.GET
interface ArticleService {
@GET("android-articles.json")
suspend fun getArticles() : List<Article>
object ArticleApi {
val articleService : ArticleService by lazy { retrofit.create(ArticleService::class.java) }
}
}

View File

@@ -38,6 +38,7 @@ import com.example.tpfilrouge.R
import com.example.tpfilrouge.ui.theme.EniButton
import com.example.tpfilrouge.ui.theme.EniPage
import com.example.tpfilrouge.ui.theme.EniTextField
import kotlinx.coroutines.flow.MutableStateFlow
class ListArticleActivity : ComponentActivity() {
@@ -73,6 +74,28 @@ fun ArticleForm(viewModel: ListArticleViewModel){
}
}
@Composable
fun ArticleCard(article: Article){
ElevatedCard(
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
modifier = Modifier.fillMaxWidth().padding(vertical = 14.dp)
) {
Row(modifier = Modifier.padding(10.dp)) {
AsyncImage(
model = article.imgPath,
contentDescription = "",
modifier = Modifier.width(82.dp).padding(horizontal = 5.dp),
placeholder = painterResource(R.drawable.reset_password_ic),
)
Column(modifier = Modifier.padding(start = 5.dp)) {
Text(article.title,
style = TextStyle(fontWeight = FontWeight.Bold, fontSize = 15.sp))
Text(article.desc)
}
}
}
}
@Composable
fun ListArticleActivityPage(viewModel: ListArticleViewModel) {
// Ecouter les changements de la liste d'article dans le ViewModel
@@ -88,26 +111,13 @@ fun ListArticleActivityPage(viewModel: ListArticleViewModel) {
textAlign = TextAlign.Center,
style = TextStyle(color = Color.White, fontSize = 28.sp))
ArticleForm(viewModel)
EniButton("Rafraichir") {
// Appeler l'api du viewmodel
viewModel.callArticlesApi()
}
LazyColumn {
items(articlesState) { article ->
ElevatedCard(
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
modifier = Modifier.fillMaxWidth().padding(vertical = 14.dp)
) {
Row(modifier = Modifier.padding(10.dp)) {
AsyncImage(
model = article.imgPath,
contentDescription = "",
modifier = Modifier.width(82.dp).padding(horizontal = 5.dp),
placeholder = painterResource(R.drawable.reset_password_ic),
)
Column(modifier = Modifier.padding(start = 5.dp)) {
Text(article.title,
style = TextStyle(fontWeight = FontWeight.Bold, fontSize = 15.sp))
Text(article.desc)
}
}
}
ArticleCard(article)
}
}
}
@@ -119,5 +129,11 @@ fun ListArticleActivityPage(viewModel: ListArticleViewModel) {
)
@Composable
fun ListArticleActivityPreview() {
ListArticleActivityPage(ListArticleViewModel())
var viewModel = ListArticleViewModel();
viewModel.articles = MutableStateFlow(listOf(
Article("Teletubies", "Meilleur série du monde", "https://avatar.iran.liara.run/public"),
Article("Velocipastor", "Meilleur film du monde, gros budget", "https://avatar.iran.liara.run/public"),
Article("Photo mouton béret paille ?", "Pourquoi", "https://avatar.iran.liara.run/public")
));
ListArticleActivityPage(viewModel)
}

View File

@@ -1,18 +1,31 @@
package com.example.tpfilrouge.article
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.demoandroid.demoapi.ArticleService
import com.example.tpfilrouge.helpers.AppDialogHelpers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class ListArticleViewModel : ViewModel() {
var articles = MutableStateFlow(listOf(
Article("Teletubies", "Meilleur série du monde", "https://avatar.iran.liara.run/public"),
Article("Velocipastor", "Meilleur film du monde, gros budget", "https://avatar.iran.liara.run/public"),
Article("Photo mouton béret paille ?", "Pourquoi", "https://avatar.iran.liara.run/public")
));
var articles = MutableStateFlow<List<Article>>(listOf());
fun addArticle(article: Article){
articles.value = articles.value + article;
}
fun callArticlesApi(){
// Afficher la popup de chargement
AppDialogHelpers.get().showDialog("Chargement des articles en cours")
viewModelScope.launch {
// Récupérer les articles via un API Web
articles.value = ArticleService.ArticleApi.articleService.getArticles();
// Fermer la popup de chargement
AppDialogHelpers.get().closeDialog();
}
}
}

View File

@@ -0,0 +1,70 @@
package com.example.tpfilrouge.helpers
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import kotlinx.coroutines.flow.MutableStateFlow
class AppDialogHelpers {
companion object {
val instance : AppDialogHelpers by lazy { AppDialogHelpers() }
fun get() : AppDialogHelpers {
return instance;
}
}
// Sert à savoir en temps réel si il faut afficher ou pas la dialog
var dialogModelData = MutableStateFlow(DialogModelData());
/**
* Afficher la popup
*/
fun showDialog(message: String){
// Forcer le rafraichissement de l'etat
dialogModelData.value = dialogModelData.value.copy(isShow = true, message = message);
}
/**
* Fermer la popup
*/
fun closeDialog() {
// Forcer le rafraichissement de l'etat
dialogModelData.value = dialogModelData.value.copy(isShow = false);
}
}
@Composable
fun ProgressDialog(){
// Je vais ecouter quand la dialog est true ou false
// Donc quand je dois afficher ou pas
val dialogModelDataState by AppDialogHelpers.get().dialogModelData.collectAsState();
if (dialogModelDataState.isShow) {
Dialog(onDismissRequest = {}){
Box(modifier = Modifier.background(
color = Color(0xFFFFFFFF),
shape = RoundedCornerShape(30.dp)
)
.padding(20.dp)) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
CircularProgressIndicator()
Text(text = dialogModelDataState.message)
}
}
}
}
}

View File

@@ -0,0 +1,4 @@
package com.example.tpfilrouge.helpers
data class DialogModelData(var isShow : Boolean = false, var message : String = "") {
}

View File

@@ -25,6 +25,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import com.example.tpfilrouge.R
import com.example.tpfilrouge.helpers.ProgressDialog
class AppTheme {
}
@@ -38,6 +39,7 @@ fun EniPage(content: @Composable () -> Unit){
// Inserer un composant dynamiquement
content()
}
ProgressDialog()
}
}
}