correcction tp 5 01
This commit is contained in:
@@ -58,4 +58,15 @@ dependencies {
|
|||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
|
|
||||||
implementation("io.coil-kt:coil-compose:2.7.0")
|
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")
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ import com.example.tpfilrouge.R
|
|||||||
import com.example.tpfilrouge.ui.theme.EniButton
|
import com.example.tpfilrouge.ui.theme.EniButton
|
||||||
import com.example.tpfilrouge.ui.theme.EniPage
|
import com.example.tpfilrouge.ui.theme.EniPage
|
||||||
import com.example.tpfilrouge.ui.theme.EniTextField
|
import com.example.tpfilrouge.ui.theme.EniTextField
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
class ListArticleActivity : ComponentActivity() {
|
class ListArticleActivity : ComponentActivity() {
|
||||||
|
|
||||||
@@ -74,22 +75,7 @@ fun ArticleForm(viewModel: ListArticleViewModel){
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ListArticleActivityPage(viewModel: ListArticleViewModel) {
|
fun ArticleCard(article: Article){
|
||||||
// Ecouter les changements de la liste d'article dans le ViewModel
|
|
||||||
val articlesState by viewModel.articles.collectAsState();
|
|
||||||
|
|
||||||
EniPage {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
modifier = Modifier.padding(40.dp)
|
|
||||||
) {
|
|
||||||
Text(text = stringResource(R.string.title_list_article_page),
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
style = TextStyle(color = Color.White, fontSize = 28.sp))
|
|
||||||
ArticleForm(viewModel)
|
|
||||||
LazyColumn {
|
|
||||||
items(articlesState) { article ->
|
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
|
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp),
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = 14.dp)
|
modifier = Modifier.fillMaxWidth().padding(vertical = 14.dp)
|
||||||
@@ -109,6 +95,30 @@ fun ListArticleActivityPage(viewModel: ListArticleViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ListArticleActivityPage(viewModel: ListArticleViewModel) {
|
||||||
|
// Ecouter les changements de la liste d'article dans le ViewModel
|
||||||
|
val articlesState by viewModel.articles.collectAsState();
|
||||||
|
|
||||||
|
EniPage {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.padding(40.dp)
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.title_list_article_page),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
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 ->
|
||||||
|
ArticleCard(article)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,5 +129,11 @@ fun ListArticleActivityPage(viewModel: ListArticleViewModel) {
|
|||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun ListArticleActivityPreview() {
|
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)
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,31 @@
|
|||||||
package com.example.tpfilrouge.article
|
package com.example.tpfilrouge.article
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
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.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class ListArticleViewModel : ViewModel() {
|
class ListArticleViewModel : ViewModel() {
|
||||||
|
|
||||||
var articles = MutableStateFlow(listOf(
|
var articles = MutableStateFlow<List<Article>>(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")
|
|
||||||
));
|
|
||||||
|
|
||||||
fun addArticle(article: Article){
|
fun addArticle(article: Article){
|
||||||
articles.value = articles.value + 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.example.tpfilrouge.helpers
|
||||||
|
|
||||||
|
data class DialogModelData(var isShow : Boolean = false, var message : String = "") {
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.tpfilrouge.R
|
import com.example.tpfilrouge.R
|
||||||
|
import com.example.tpfilrouge.helpers.ProgressDialog
|
||||||
|
|
||||||
class AppTheme {
|
class AppTheme {
|
||||||
}
|
}
|
||||||
@@ -38,6 +39,7 @@ fun EniPage(content: @Composable () -> Unit){
|
|||||||
// Inserer un composant dynamiquement
|
// Inserer un composant dynamiquement
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
ProgressDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user