Compare commits

...

15 Commits

@ -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 {
@ -51,14 +52,35 @@ android {
dependencies { dependencies {
// Retrofit
implementation ("com.squareup.retrofit2:retrofit: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.media3:media3-session:1.3.0")
implementation("com.google.android.exoplayer:exoplayer-core:2.19.1")
implementation ("com.google.android.exoplayer:exoplayer-ui:2.19.1")
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")
implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3") implementation("androidx.compose.material3:material3")
implementation("com.google.firebase:firebase-crashlytics-buildtools:2.9.9")
implementation("androidx.navigation:navigation-compose:2.8.0-alpha05")
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
@ -69,4 +91,5 @@ dependencies {
implementation("io.coil-kt:coil-compose:1.4.0") implementation("io.coil-kt:coil-compose:1.4.0")
} }

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-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"
>
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -11,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,268 +1,63 @@
package com.example.veraxapplication package com.example.veraxapplication
import android.os.Build import ArticlesViewModel
import IUsersDataManager
import StubUsers
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.ui.graphics.Color
import androidx.compose.material3.Text
import androidx.compose.ui.res.colorResource
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Person
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.compose.runtime.setValue import com.example.veraxapplication.modele.IArticlesDataManager
import androidx.compose.ui.Alignment
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter
import coil.size.Scale
import com.example.veraxapplication.articles.IArticlesDataManager
import com.example.veraxapplication.articles.StubArticles import com.example.veraxapplication.articles.StubArticles
import com.example.veraxapplication.data.Article import com.example.veraxapplication.modele.api.UsersViewModel
import com.example.veraxapplication.data.Paragraph import com.example.veraxapplication.navigation.VeraxNavHost
import com.example.veraxapplication.ui.theme.Salmon import com.example.veraxapplication.ui.connexion.AfficherForm
import com.example.veraxapplication.ui.topBar.TopBarVerax
// doc navBar: https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#TopAppBar(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function0,kotlin.Function1,androidx.compose.foundation.layout.WindowInsets,androidx.compose.material3.TopAppBarColors,androidx.compose.material3.TopAppBarScrollBehavior) // doc navBar: https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#TopAppBar(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function0,kotlin.Function1,androidx.compose.foundation.layout.WindowInsets,androidx.compose.material3.TopAppBarColors,androidx.compose.material3.TopAppBarScrollBehavior)
// doc compose, pleins de trucs: https://developer.android.com/jetpack/compose/text?hl=fr // doc compose, pleins de trucs: https://developer.android.com/jetpack/compose/text?hl=fr
//doc couleur background pas finie: https://developer.android.com/jetpack/compose/components/scaffold //doc couleur background pas finie: https://developer.android.com/jetpack/compose/components/scaffold
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
// 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"))))
var dataManager: IArticlesDataManager = StubArticles()
var articles = dataManager.getDerniersArticles(4)
var theme = listOf("Economique", "Culture", "Politique", "Faits divers")
var color = Color(0xFF00FF00)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
VeraxContent()
// articles?.forEach({ a -> println(a)})
articles?.forEach({ a ->
Log.println(Log.ASSERT, "debug articles", a.toString())
})
} }
} }
}
// Il faudrait mettre ca dans un fichier appart mais je connais plus les conventions ... @Composable
@OptIn(ExperimentalMaterial3Api::class) fun VeraxContent() {
@Composable
fun TopBarVerax(theme: List<String>, article: List<Article>) {
var leMenu by remember {
mutableStateOf(false)
}
Row() {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
text = "Verax",
style = TextStyle(fontSize = 35.sp),
color = colorResource(R.color.red),
textAlign = TextAlign.Center,
/*backcolor = topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer),*/ //version recommandée par le prof
modifier = Modifier.fillMaxWidth()
)
},
navigationIcon = {
IconButton(onClick = { /* action() */ }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Retour",
Modifier.size(30.dp)
)
}
},
actions = {
IconButton(onClick = { leMenu = !leMenu }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "Menu",
Modifier.size(35.dp)
)
}
DropdownMenu(
expanded = leMenu, onDismissRequest = { leMenu = false },
modifier = Modifier
.background(Color.hsl(0.08F, 1F, 0.96F))
) {
theme.sorted().forEach {
DropdownMenuItem(
text = {
Text(
it,
style = TextStyle(fontSize = 25.sp),
modifier = Modifier
.padding(10.dp)
)
},
onClick = { /* faut un moyen d'appeler une methode diff pour chaque, ca doit etre faisable facilement */ }
)
}
}
}
)
},
bottomBar = {
// Faudrait pouvoir faire un flex sur les boutons parce que là ils sont juste côte à côte
BottomAppBar(containerColor = Color.Black, contentColor = Color.White) {
IconButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Filled.Home,
contentDescription = "Home",
Modifier.size(35.dp)
)
}
IconButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Filled.Person,
contentDescription = "Account",
Modifier.size(35.dp)
)
}
}
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
AffichageUnArticle(article = article)
} // Initialiser les données ou observer les données du ViewModel
} var dataManager: IArticlesDataManager = StubArticles()
} var articles = dataManager.getDerniersArticles(4)
}
var usersManager: IUsersDataManager = StubUsers()
var users = usersManager.getUsers();
@Composable // Observer les données du ViewModel
fun AffichageUnArticle(article: List<Article>) { val articlesViewModel: ArticlesViewModel = viewModel()
Column() {
for (e in article) {
DisplayTitle(title = e.Title)
DisplayHeader(
author = e.Author,
description = e.Description,
lectureTime = e.LectureTime
)
DisplayImage(image = e.Image)
DisplayContentArticle(content = e.Content)
}
}
}
@Composable // Observez les articles du ViewModel
fun DisplayImage(image: String) { val articlesApi by articlesViewModel.articles.observeAsState(initial = articles)
Log.d("DisplayImage", "Chargement de l'image à partir de l'URL : $image")
val painter = rememberImagePainter( val usersViewModel: UsersViewModel = viewModel()
data = image,
builder = {
scale(Scale.FILL)
}
)
Log.d("DisplayImage", "Painter créé avec succès") val usersApi by usersViewModel.users.observeAsState(initial = users)
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
androidx.compose.foundation.Image(
painter = painter,
contentDescription = null,
modifier = Modifier.fillMaxSize(),
)
}
}
var theme = listOf("Economique", "Culture", "Politique", "Faits divers")
@Composable
fun DisplayHeader(author: String, description: String, lectureTime: String) {
Box(
modifier = Modifier
.fillMaxWidth()
.border(width = 1.dp, color = Color.Black, shape = RoundedCornerShape(10.dp))
.clip(RoundedCornerShape(10.dp))
.background(Salmon)
.padding(10.dp)
) {
Column() {
Text(text = author)
Text(text = description)
Text(text = "Lecture Time: " + lectureTime + " minutes")
}
}
}
@Composable TopBarVerax(articles = articlesApi, theme = theme, articlesStub= articles)
fun DisplayTitle(title: String) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = title, fontFamily = FontFamily.Serif, fontSize = 30.sp)
}
}
@Composable
fun DisplayContentArticle(content: List<Paragraph>) {
Column {
for (e in content) {
Text(
text = e.Content,
fontSize = 15.sp,
fontFamily = FontFamily.Serif,
textAlign = TextAlign.Justify,
modifier = Modifier.padding(10.dp)
)
}
}
}
} }

@ -1,5 +1,5 @@
package com.example.veraxapplication.data package com.example.veraxapplication.data
/*
data class Article( data class Article(
var Title : String, var Title : String,
var Description : String, var Description : String,
@ -8,3 +8,4 @@ data class Article(
var Content : List<Paragraph>, var Content : List<Paragraph>,
var LectureTime : String var LectureTime : String
) )
*/

@ -1,5 +1,6 @@
package com.example.veraxapplication.data package com.example.veraxapplication.data
/*
data class Paragraph( data class Paragraph(
var Content : String, var Content : String,
) )
*/

@ -1,4 +1,4 @@
package com.example.veraxapplication.articles package com.example.veraxapplication.modele
import com.example.veraxapplication.modele.articles.Article import com.example.veraxapplication.modele.articles.Article
@ -7,5 +7,5 @@ interface IArticlesDataManager {
val allArticles: List<Any?>? val allArticles: List<Any?>?
fun getArticle(id: Int): Article? fun getArticle(id: Int): Article?
fun getDerniersArticles(nbArticles: Int): List<Article?>? fun getDerniersArticles(nbArticles: Int): List<Article>
} }

@ -0,0 +1,9 @@
import com.example.veraxapplication.modele.user.User
interface IUsersDataManager {
val allUsers: List<Any?>?
fun getUser(pseudo : String): User?
fun getUsers(): List<User>
}

@ -0,0 +1,53 @@
package com.example.veraxapplication.modele.api
import com.example.veraxapplication.modele.articles.Article
import com.google.gson.annotations.SerializedName
data class ArticleDTO (
@SerializedName("id")
val id: Int,
@SerializedName("titre")
val titre: String,
@SerializedName("description")
val description: String,
@SerializedName("temps")
val temps: String,
@SerializedName("date")
val date: String,
@SerializedName("auteur")
val auteur: String,
@SerializedName("categorie")
val categorie: String,
@SerializedName("imagePrincipale")
val imagePrincipale: String,
@SerializedName("note")
val note: String,
) {
fun toModel(): Article {
return Article(
id,
titre,
description,
temps,
date,
auteur,
categorie,
imagePrincipale,
note,
)
}
}
data class ContenuDTO (
@SerializedName("id")
val id: Int,
@SerializedName("typeContenu")
val typeContenu: String,
@SerializedName("titre")
val titre: String,
@SerializedName("texte")
val texte: String,
)

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

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

@ -0,0 +1,11 @@
package com.example.veraxapplication.modele.api
import com.example.veraxapplication.modele.user.User
import retrofit2.Call
import retrofit2.http.GET
interface IUserService {
@GET("users")
suspend fun getUsers(): List<UserDTO>
}

@ -0,0 +1,32 @@
package com.example.veraxapplication.modele.api
import com.example.veraxapplication.modele.user.User
import com.google.gson.annotations.SerializedName
data class UserDTO (
@SerializedName("pseudo")
val pseudo: String,
@SerializedName("mdp")
val mdp: String,
@SerializedName("mail")
val mail: String,
@SerializedName("nom")
val nom: String,
@SerializedName("prenom")
val prenom: String,
@SerializedName("role")
val role: String,
) {
fun toModel(): User {
return User(
pseudo,
mdp,
mail,
nom,
prenom,
role,
)
}
}

@ -0,0 +1,63 @@
package com.example.veraxapplication.modele.api
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.articles.Article
import com.example.veraxapplication.modele.user.User
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClientUser {
private const val BASE_URL = "https://codefirst.iut.uca.fr/containers/Verax-verax-api"
private val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
private val httpClient = OkHttpClient.Builder().apply {
addInterceptor(logging)
}.build()
val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
// interface UserApiService {
// @GET("users")
// suspend fun getUsers() : List<User>
// }
}
class UsersViewModel : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> = _users
private val service = ArticleApiClient.retrofit.create(IUserService::class.java)
init {
loadUsers()
}
fun loadUsers() {
viewModelScope.launch {
try {
val usersDto = service.getUsers() // Pas besoin d'appeler .execute()
// Convertissez les DTO en modèles de données si nécessaire
_users.value = usersDto.map { it.toModel() }
} catch (e: Exception) {
Log.e("UsersViewModel", "Erreur lors du chargement des users", e)
}
}
}
}

@ -9,10 +9,11 @@ class Article(
val temps: String, val temps: String,
val date: String, val date: String,
val auteur: String, val auteur: String,
val imagePrincipale: String val categorie: String,
val imagePrincipale: String,
val note: String
) { ) {
val note = 1.0 public val lContenus: MutableList<Contenu> = java.util.ArrayList()
private val lContenus: MutableList<Contenu> = java.util.ArrayList()
init { init {
// Initialisation des contenus si nécessaire // Initialisation des contenus si nécessaire
@ -30,4 +31,6 @@ class Article(
override fun toString(): String { override fun toString(): String {
return "Article(id=$id, titre='$titre', description='$description', temps='$temps', date='$date', auteur='$auteur', imagePrincipale='$imagePrincipale', note=$note, contenus=${lContenus.size} contenus)" return "Article(id=$id, titre='$titre', description='$description', temps='$temps', date='$date', auteur='$auteur', imagePrincipale='$imagePrincipale', note=$note, contenus=${lContenus.size} contenus)"
} }
} }

@ -1,13 +1,10 @@
package com.example.veraxapplication.articles package com.example.veraxapplication.articles
import android.os.Build import com.example.veraxapplication.modele.IArticlesDataManager
import androidx.annotation.RequiresApi
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.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 java.time.LocalDate
import kotlin.math.min
class StubArticles() : IArticlesDataManager { class StubArticles() : IArticlesDataManager {
@ -28,10 +25,12 @@ 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", "Siwa",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219554341388816437/stub1.webp?ex=660bb97d&is=65f9447d&hm=3e1e8d3372ae897fa4e2aa1ec730d976d74b35fce96cb8d78d6f9239e2836564&" "Politique",
) "https://cdn.discordapp.com/attachments/1150826798549049554/1219554341388816437/stub1.webp?ex=660bb97d&is=65f9447d&hm=3e1e8d3372ae897fa4e2aa1ec730d976d74b35fce96cb8d78d6f9239e2836564&",
"12"
)
article1.remplirArticle(contenuMap["article1"]) article1.remplirArticle(contenuMap["article1"])
lArticles!!.add(article1) lArticles!!.add(article1)
@ -41,9 +40,11 @@ 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", "Siwa",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219555874339815454/stub2.webp?ex=660bbaea&is=65f945ea&hm=80aef945e8410b18395c716fdd19265608f7b1263731192d5c69f807fce9e944&" "Politique",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219555874339815454/stub2.webp?ex=660bbaea&is=65f945ea&hm=80aef945e8410b18395c716fdd19265608f7b1263731192d5c69f807fce9e944&",
"12"
) )
article2.remplirArticle(contenuMap["article5"]) article2.remplirArticle(contenuMap["article5"])
lArticles!!.add(article2) lArticles!!.add(article2)
@ -54,9 +55,11 @@ 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", "Siwa",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219547864196317225/stub1.webp?ex=660bb374&is=65f93e74&hm=a9e5dd48faa3ae68c358309af8949c46dfd4dea9c4d6e3d845d707784e5341cf&" "Politique",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219547864196317225/stub1.webp?ex=660bb374&is=65f93e74&hm=a9e5dd48faa3ae68c358309af8949c46dfd4dea9c4d6e3d845d707784e5341cf&",
"12"
) )
article3.remplirArticle(contenuMap["article4"]) article3.remplirArticle(contenuMap["article4"])
lArticles!!.add(article3) lArticles!!.add(article3)
@ -67,9 +70,11 @@ 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", "Luthen",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219560686254817290/stub1.webp?ex=660bbf65&is=65f94a65&hm=021bd8c90c89347f31373468cc7a03ae15f1d3f9988a5b4325149c6df938d7bb&" "Politique",
"https://cdn.discordapp.com/attachments/1150826798549049554/1219560686254817290/stub1.webp?ex=660bbf65&is=65f94a65&hm=021bd8c90c89347f31373468cc7a03ae15f1d3f9988a5b4325149c6df938d7bb&",
"12"
) )
article4.remplirArticle(contenuMap["article6"]) article4.remplirArticle(contenuMap["article6"])
lArticles!!.add(article4) lArticles!!.add(article4)
@ -88,12 +93,12 @@ class StubArticles() : IArticlesDataManager {
article1Contenus.add( article1Contenus.add(
ContenuParagraphe( ContenuParagraphe(
1, "Le mystérieux cygne noir.", 1, "Le mystérieux cygne noir.",
"Sous les traits dun mystérieux cygne noir, un objet vidéo non identifié plane dans la galaxie \n" + "Sous les traits dun mystérieux cygne noir, un objet vidéo non identifié plane dans la galaxie " +
" médiatique. Ambiance Star Wars. Après une musique lancinante, sur fond noir et fumée grisâtre,\n" + " médiatique. Ambiance Star Wars. Après une musique lancinante, sur fond noir et fumée grisâtre," +
" un invité apparaît, et la voix dun intervieweur-mystère situé hors champ linvite à se\n" + " un invité apparaît, et la voix dun intervieweur-mystère situé hors champ linvite à se" +
" présenter « succinctement ». Cest ainsi quon entre dans le monde de ThinkerView, \n" + " présenter « succinctement ». Cest ainsi quon entre dans le monde de ThinkerView," +
" chaîne qui a commencé à diffuser en janvier 2013 sur YouTube. Ici, pas de publicité,\n" + " chaîne qui a commencé à diffuser en janvier 2013 sur YouTube. Ici, pas de publicité," +
" pas de montage, pas deffets de lumière. Le calme, peut-être pour annoncer la tempête." " pas de montage, pas deffets de lumière. Le calme, peut-être pour annoncer la tempête."
) )
) )
article1Contenus.add( article1Contenus.add(
@ -106,14 +111,14 @@ class StubArticles() : IArticlesDataManager {
article1Contenus.add( article1Contenus.add(
ContenuParagraphe( ContenuParagraphe(
2, "Penser, réfléchir et s'exprimer librement.", 2, "Penser, réfléchir et s'exprimer librement.",
("Dernier carton en date : un entretien de deux heures avec Juan Branco, lavocat du gilet \n" + ("Dernier carton en date : un entretien de deux heures avec Juan Branco, lavocat du gilet" +
" jaune Maxime Nicolle et « conseiller juridique » de Wikileaks. Quelques jours avant lui,\n" + " jaune Maxime Nicolle et « conseiller juridique » de Wikileaks. Quelques jours avant lui," +
" cétait au tour de François Boulo, autre avocat inscrit au barreau de Rouen et lun des\n" + " cétait au tour de François Boulo, autre avocat inscrit au barreau de Rouen et lun des" +
" porte-parole des « gilets jaunes ». « Ici, les gens ont vraiment le temps de développer \n" + " porte-parole des « gilets jaunes ». « Ici, les gens ont vraiment le temps de développer" +
" leurs idées, confie Boulo. Il faut pouvoir écouter une pensée complète, sans être interrompu.\n" + " leurs idées, confie Boulo. Il faut pouvoir écouter une pensée complète, sans être interrompu." +
" » Aux yeux de ce fils dune famille de droite populaire (paysans et commerçants), ThinkerView \n" + " » Aux yeux de ce fils dune famille de droite populaire (paysans et commerçants), ThinkerView" +
" a réalisé ce dont Pierre Bourdieu avait rêvé. Sil sabreuve à cette source depuis « un an ou \n" + " a réalisé ce dont Pierre Bourdieu avait rêvé. Sil sabreuve à cette source depuis « un an ou" +
" deux », en réalité, ce nest pas lui qui la trouvée, mais linverse. Magie des algorithmes.") " deux », en réalité, ce nest pas lui qui la trouvée, mais linverse. Magie des algorithmes.")
) )
) )
article1Contenus.add( article1Contenus.add(
@ -126,19 +131,19 @@ class StubArticles() : IArticlesDataManager {
article1Contenus.add( article1Contenus.add(
ContenuParagraphe( ContenuParagraphe(
3, "Une alternative dans un monde aux informations formatées", 3, "Une alternative dans un monde aux informations formatées",
("\\\"Nous faisons des interviews aux perspectives alternatives dans un monde aux informations formatées\\\",\n" + ("\\\"Nous faisons des interviews aux perspectives alternatives dans un monde aux informations formatées\\\"," +
" explique le site Thinkerview. La marque a adopté un cygne noir comme logo, un clin d'œil à la théorie \n" + " explique le site Thinkerview. La marque a adopté un cygne noir comme logo, un clin d'œil à la théorie" +
" du cygne noir (expliquée dans cet article de Challenges), soit un événement qui a peu de \n" + " du cygne noir (expliquée dans cet article de Challenges), soit un événement qui a peu de" +
" chances de se produire mais qui, s'il se produit, a des conséquences considérables.\n" + " chances de se produire mais qui, s'il se produit, a des conséquences considérables." +
"\n" + "\n" +
" Les invités viennent d'horizons divers avec une petite préférence pour les \n" + " Les invités viennent d'horizons divers avec une petite préférence pour les" +
" intellectuels iconoclastes et les contestataires de tous bords, de l'ancien \n" + " intellectuels iconoclastes et les contestataires de tous bords, de l'ancien" +
" ministre grec Yanis Varoufakis à l'historien et essayiste Emmanuel Todd, en\n" + " ministre grec Yanis Varoufakis à l'historien et essayiste Emmanuel Todd, en" +
" passant par les journalistes Natacha Polony et Laurent Obertone ou encore \n" + " passant par les journalistes Natacha Polony et Laurent Obertone ou encore" +
" la coqueluche des \\\"gilets jaunes\\\" Etienne Chouard. \\\"On est au milieu de \n" + " la coqueluche des \\\"gilets jaunes\\\" Etienne Chouard. \\\"On est au milieu de" +
" toutes les communautés qui s'écharpent sur internet, de l'extrême droite à\n" + " toutes les communautés qui s'écharpent sur internet, de l'extrême droite à" +
" l'extrême gauche, explique Sky. On cherche à créer un terrain neutre pour \n" + " l'extrême gauche, explique Sky. On cherche à créer un terrain neutre pour" +
" que tout le monde puisse échanger.") " que tout le monde puisse échanger.")
) )
) )
article1Contenus.add( article1Contenus.add(
@ -430,10 +435,8 @@ class StubArticles() : IArticlesDataManager {
return null // Retourne null si lArticles est null return null // Retourne null si lArticles est null
} }
override fun getDerniersArticles(nbArticles: Int): List<Article>? { override fun getDerniersArticles(nbArticles: Int): List<Article> {
return lArticles?.let { return lArticles?.takeIf { it.isNotEmpty() }?.take(nbArticles) ?: emptyList()
it.take(min(nbArticles, it.size))
}
} }
} }

@ -25,6 +25,6 @@ class ContenuMedia(id: Int, var titre: String, var lien: String) : Contenu(id) {
} }
override fun toString(): String { override fun toString(): String {
return "ContenuMedia(id=$id, typeContenu='$typeContenu', titre='$titre', lien='$lien')" return "$lien\n\n"
} }
} }

@ -15,6 +15,6 @@ class ContenuParagraphe(id: Int, var titre: String, var texte: String) : Contenu
} }
override fun toString(): String { override fun toString(): String {
return "ContenuParagraphe(id=$id, typeContenu='$typeContenu', titre='$titre', texte='$texte')" return "$texte \n\n"
} }
} }

@ -0,0 +1,78 @@
import com.example.veraxapplication.modele.IArticlesDataManager
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.ContenuParagraphe
import com.example.veraxapplication.modele.user.User
class StubUsers() : IUsersDataManager {
private var lUsers: MutableList<User>? = null
init {
chargerUsers()
}
private fun chargerUsers() {
lUsers = java.util.ArrayList<User>()
val user1 = User (
"NoaSil",
"1234",
"",
"Sillard",
"Noa",
"Admin"
)
lUsers!!.add(user1)
val user2 = User (
"Sha",
"1234",
"",
"Cascarra",
"Shana",
"Admin"
)
lUsers!!.add(user2)
val user3 = User (
"TonyF",
"1234",
"tony@gmail.com",
"Fages",
"Tony",
"Admin"
)
lUsers!!.add(user3)
val user4 = User (
"JeanSwaggLaPuissance63",
"1234",
"jean.lapuissance@gmail.com",
"Marcillac",
"Jean",
"Admin"
)
lUsers!!.add(user4)
}
override val allUsers: List<User>?
get() = lUsers
override fun getUser(pseudo : String): User? {
println("Passage dans getUser avec comme pseudo : $pseudo")
lUsers?.let {
println("Nombre d'utilisateurs disponibles : ${it.size}")
val userARenvoyer: User? = it.find { user -> user.pseudo == pseudo }
return userARenvoyer
}
return null
}
override fun getUsers(): List<User>
{
return lUsers?.takeIf { it.isNotEmpty() }?.take(lUsers!!.size) ?: emptyList()
}
}

@ -0,0 +1,3 @@
package com.example.veraxapplication.modele.user
data class User(val pseudo : String, val mdp : String, val mail : String, val nom : String, val prenom : String, val role : String)

@ -0,0 +1,46 @@
package com.example.veraxapplication.navigation
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.veraxapplication.modele.articles.Article
import com.example.veraxapplication.ui.article.AffichageLesArticles
import com.example.veraxapplication.ui.article.AfficherArticle
@Composable
fun VeraxNavHost(articles : List<Article>, navController: NavHostController, articlesStub: List<Article>) {
NavHost(
navController = navController,
startDestination = "accueil"
){
composable(route="accueil"){
AffichageLesArticles(
articles = articles,
goToArticle = {
navController.navigate("article/${it.id}")
}
)
}
composable(
route="article/{articleid}",
arguments= listOf(navArgument("articleid"){ type= NavType.IntType})
){
it.arguments?.getInt("articleid")?.let { articleid ->
articlesStub.find { it.id == articleid }?.let {
AfficherArticle(
e = it
)
}
}
}
}
}

@ -0,0 +1,189 @@
package com.example.veraxapplication.ui.article
import VideoPlayer
import android.util.Log
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter
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.ContenuParagraphe
import com.example.veraxapplication.ui.theme.Salmon
@Composable
fun AffichageLesArticles(articles : List<Article>, goToArticle: (Article) -> Unit){
Column(modifier = Modifier.verticalScroll(rememberScrollState())){
for(article in articles){
Box {
AffichageUnArticleInfo(e = article, goToArticle)
}
}
}
}
@Composable
fun AffichageUnArticleInfo(e : Article, goToArticle: (Article) -> Unit){
Column(modifier = Modifier
.padding(7.dp)
.border(width = 1.dp, color = Color.Black, shape = RoundedCornerShape(10.dp))
.padding(5.dp)) {
Text(text = e.titre, fontFamily = FontFamily.Serif, fontSize = 30.sp)
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 : "+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(
painter = rememberImagePainter(
data = e.imagePrincipale
),
contentScale = ContentScale.Crop,
contentDescription = null,
modifier = Modifier
.size(350.dp)
.align(Alignment.CenterHorizontally)
.padding(5.dp, 35.dp)
)
Button(onClick = { goToArticle(e)},
colors = ButtonDefaults.buttonColors(
containerColor = Salmon,
contentColor = Color.Black
),
border = BorderStroke(1.dp, Color.Black),
modifier = Modifier
.align(Alignment.CenterHorizontally)
) {
Text(text = "Voir plus")
}
}
}
@Composable
fun AfficherArticle(e : Article){
Column(modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(7.dp)
.padding(5.dp)) {
Text(text = e.titre, fontFamily = FontFamily.Serif, fontSize = 30.sp)
// Affichage des informations de l'article
DisplayHeader(author = e.auteur, description = e.description, lectureTime = e.temps)
Image(
painter = rememberImagePainter(
data = e.imagePrincipale
),
contentScale = ContentScale.Crop,
contentDescription = null,
modifier = Modifier
.size(350.dp)
.align(Alignment.CenterHorizontally)
.padding(5.dp, 35.dp)
)
// Affichage contenus
DisplayContenu(e.lContenus)
}
}
@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 -> {
Log.d("Img", contenu.titre + " " + contenu.lien)
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" -> {
Text(
text = "Ici une video ..." + contenu.toString(),
fontSize = 15.sp,
textAlign = TextAlign.Start
)
VideoPlayer(videoUrl = contenu.lien)
}
}
}
is ContenuParagraphe -> {
Text(text = contenu.titre, fontSize = 20.sp, fontWeight = FontWeight.Bold)
Text(text = contenu.texte, fontSize = 16.sp, textAlign = TextAlign.Start)
}
}
}
}
}

@ -0,0 +1,41 @@
import android.view.ViewGroup
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.ui.PlayerView
@Composable
fun VideoPlayer(videoUrl : String){
val context = LocalContext.current
val player = ExoPlayer.Builder(context).build()
val playerView = PlayerView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
val mediaItem = MediaItem.Builder()
.setUri(videoUrl)
.build()
player.setMediaItem(mediaItem)
player.prepare()
player.play()
AndroidView(
factory = { context -> playerView },
modifier = Modifier.fillMaxSize(),
update = { view ->
view.player = player
}
)
}

@ -0,0 +1,23 @@
package com.example.veraxapplication.ui.connexion
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.veraxapplication.modele.user.User
@Composable
fun AfficherForm(users : List<User>) {
var pseudo = "DEFAULT"
var mdp = "DEFAULT"
Column {
TextField(value = "Pseudo", onValueChange = { value -> pseudo = value }, modifier = Modifier.padding(5.dp))
TextField(value = "Mot de passe", onValueChange = { value -> mdp = value }, modifier = Modifier.padding(5.dp))
for (u in users) {
Text(text = u.pseudo)
}
}
}

@ -8,5 +8,5 @@ val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4) val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71) val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260) val Orange = Color(0xFFFADCD1)
val Salmon = Color(0xFFE9967A) val Salmon = Color(0xFFE9967A)

@ -22,9 +22,9 @@ private val DarkColorScheme = darkColorScheme(
) )
private val LightColorScheme = lightColorScheme( private val LightColorScheme = lightColorScheme(
primary = Purple40, primary = Salmon,
secondary = PurpleGrey40, secondary = PurpleGrey40,
tertiary = Pink40 tertiary = Salmon
/* Other default colors to override /* Other default colors to override
background = Color(0xFFFFFBFE), background = Color(0xFFFFFBFE),

@ -0,0 +1,125 @@
package com.example.veraxapplication.ui.topBar
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.example.veraxapplication.R
import com.example.veraxapplication.modele.articles.Article
import com.example.veraxapplication.navigation.VeraxNavHost
import com.example.veraxapplication.ui.theme.Orange
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopBarVerax(theme: List<String>, articles: List<Article>, articlesStub : List<Article>) {
/*var leMenu by remember { mutableStateOf(false) }*/
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
Row( modifier = Modifier.background(Color.Blue).fillMaxSize()) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(
//text = R.string.app_name,
text= "Verax",
style = TextStyle(fontSize = 35.sp),
color = colorResource(R.color.red),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
},
navigationIcon = { if (navBackStackEntry?.destination?.route != "accueil"){
IconButton(onClick = { navController.popBackStack() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Retour",
Modifier.size(30.dp)
)
}
}
}/*,
actions = {
IconButton(onClick = { leMenu = !leMenu }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "Menu",
Modifier.size(35.dp)
)
}
DropdownMenu(
expanded = leMenu, onDismissRequest = { leMenu = false },
modifier = Modifier
.border(
width = 1.dp,
color = Color.Black,
shape = RoundedCornerShape(10.dp)
)
.background(Orange)
) {
theme.sorted().forEach {
DropdownMenuItem(
text = {
Text(
it,
style = TextStyle(fontSize = 25.sp),
modifier = Modifier
.padding(10.dp)
)
},
onClick = { }
)
}
}
}*/
)
}
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
VeraxNavHost(articles = articles, navController, articlesStub= articlesStub)
}
}
}
}

@ -1,3 +1,3 @@
<resources> <resources>
<string name="app_name">VeraxApplication</string> <string name="app_name">Verax</string>
</resources> </resources>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">181.214.189.133</domain>
<domain includeSubdomains="true">codefirst.iut.uca.fr/containers/Verax-verax-api</domain>
</domain-config>
</network-security-config>
Loading…
Cancel
Save