Lucie GOIGOUX 1 year ago
commit e8bf8156c5

@ -40,7 +40,7 @@ android {
compose = true compose = true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.5.11" kotlinCompilerExtensionVersion = "1.5.1"
} }
packaging { packaging {
resources { resources {
@ -50,6 +50,8 @@ android {
} }
dependencies { dependencies {
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
implementation("androidx.compose.material:material:1.6.4") implementation("androidx.compose.material:material:1.6.4")
implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")

@ -0,0 +1,28 @@
package sae.android.sae_2a.VM
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import sae.android.sae_2a.data.LoginRepository
import sae.android.sae_2a.data.LoginResponse
class LoginViewModel(
private val loginRepository: LoginRepository
): ViewModel() {
fun login( token: String) {
viewModelScope.launch {
val jsonBody = "{ token: \"$token\"}"
val result = try {
loginRepository.makeLoginRequest(jsonBody)
} catch(e: Exception) {
sae.android.sae_2a.data.Result.Error(Exception("Network request failed"))
}
when (result) {
is sae.android.sae_2a.data.Result.Success<LoginResponse> -> print("sucess")
else -> ("error")
}
}
}
}

@ -0,0 +1,8 @@
package sae.android.sae_2a.data
data class Group(
val id : Long,
val num : Int,
val year : Int,
val sector : String
)

@ -0,0 +1,47 @@
package sae.android.sae_2a.data
import android.provider.ContactsContract.CommonDataKinds.Website.URL
import org.json.JSONObject
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
class LoginResponseParser {
fun parse(inputStream: InputStream): LoginResponse {
val responseString = inputStream.bufferedReader().use { it.readText() }
// Supposons que la réponse du serveur soit un JSON contenant une clé "token"
val jsonObject = JSONObject(responseString)
val token = jsonObject.getString("token")
return LoginResponse(token)
}
}
data class LoginResponse(
val token: String
)
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
class LoginRepository(private val responseParser: LoginResponseParser) {
private val loginUrl = "https://codefirst.iut.uca.fr/containers/antoinejourdain-api_container/Auth/token"
// Function that makes the network request, blocking the current thread
fun makeLoginRequest(
jsonBody: String
): Result<LoginResponse> {
val url = URL(loginUrl)
(url.openConnection() as? HttpURLConnection)?.run {
requestMethod = "POST"
setRequestProperty("Content-Type", "application/json; utf-8")
setRequestProperty("Accept", "application/json")
doOutput = true
outputStream.write(jsonBody.toByteArray())
return Result.Success(responseParser.parse(inputStream))
}
return Result.Error(Exception("Cannot open HttpURLConnection"))
}
}

@ -0,0 +1,23 @@
package sae.android.sae_2a.data
import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Menu
import androidx.compose.ui.graphics.vector.ImageVector
import sae.android.sae_2a.R
sealed class Screen(val route: String, @StringRes val resourceId: Int, val image: ImageVector) {
object Profile : Screen("profile", R.string.profilePicture, Icons.Filled.AccountBox )
object Home : Screen("profileScreen", R.string.home, Icons.Filled.Home )
object VocabularyList : Screen("VocabularyListScreen", R.string.voc_image_description, Icons.Filled.Menu)
object Game : Screen("GameScreen", R.string.game ,Icons.Filled.Home)
object Register : Screen("RegisterScreen", R.string.register,Icons.Filled.Home )
object Login : Screen("LoginScreen", R.string.logIn , Icons.Filled.Home)
}

@ -1,7 +1,8 @@
package sae.android.sae_2a.data package sae.android.sae_2a.data
data class Vocabulary( data class Vocabulary(
val name: String, val word : String,
val aut: String?, val LangueName: String,
val words: Map<String, String> val words: Map<String, String>
) )

@ -0,0 +1,27 @@
package sae.android.sae_2a.service
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object ApiClient {
private val BASE_URL: String = "https://codefirst.iut.uca.fr/containers/antoinejourdain-api_container/api/v1/"
private val httpClient : OkHttpClient by lazy {
OkHttpClient.Builder().build()
}
private val gson : Gson by lazy {
GsonBuilder().setLenient().create()
}
private val retrofit : Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
}

@ -0,0 +1,22 @@
package sae.android.sae_2a.service
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Path
import sae.android.sae_2a.data.Group
import sae.android.sae_2a.data.Vocabulary
interface GroupService {
@GET("Group")
suspend fun getVocabulary(@Body index : Int, @Body count : Int) : Response<MutableList<Group>>
@GET("Group/{id}")
suspend fun getVocById(@Path("id") id : Int) : Response<Group>
}

@ -0,0 +1,37 @@
package sae.android.sae_2a.service
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
import sae.android.sae_2a.data.Vocabulary
interface UserService {
@GET("Vocabulary")
suspend fun getVocabulary(@Body index : Int, @Body count: Int) : Response<MutableList<Vocabulary>>
@GET("Vocabulary/{word}")
suspend fun getVocByWord(@Path("word") word : String) : Response<Vocabulary>
@PUT("Vocabulary/{vocabulary}")
suspend fun updateVoc(@Path("vocabulary") vocabulary: Vocabulary) : Response<Vocabulary>
@DELETE("Vocabulary/{word}")
suspend fun deleteVoc(@Path("word") word: String) : Response<Vocabulary>
@POST("Vocabulary/{vocabulary}")
suspend fun addVoc(@Path("vocabulary") vocabulary: Vocabulary) : Response<Vocabulary>
@GET("Vocabulary/langue/{langue}")
suspend fun getByLangue(@Path("langue") langue : String,@Body index : Int, @Body count : Int) : Response<Vocabulary>
@POST("Vocabulary/AddTranslation")
suspend fun addTranslation(@Query("vocId") vocId : String,@Query("translationId") translationId : Long ) : Response<Vocabulary>
}

@ -13,10 +13,12 @@ import androidx.compose.foundation.layout.wrapContentWidth
//import androidx.compose.material.BottomNavigation //import androidx.compose.material.BottomNavigation
//import androidx.compose.material.BottomNavigationItem //import androidx.compose.material.BottomNavigationItem
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -26,40 +28,69 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import sae.android.sae_2a.R import sae.android.sae_2a.R
import sae.android.sae_2a.data.Screen
import sae.android.sae_2a.game.VocabularyScreen import sae.android.sae_2a.game.VocabularyScreen
@Preview
val items = listOf(
Screen.Game,
Screen.VocabularyList,
Screen.Profile
)
@Preview
@Composable @Composable
fun MyApp() { fun MyApp() {
val navController = rememberNavController() val navController = rememberNavController()
/* Scaffold( Scaffold(
bottomBar = { bottomBar = {
BottomNavigation { val navBackStackEntry by navController.currentBackStackEntryAsState()
BottomNavigationItem(selected = , onClick = { /*TODO*/ }, icon = { /*TODO*/ }){ val currentDestination = navBackStackEntry?.destination
if (currentDestination?.route !in listOf(Screen.Home.route, Screen.Login.route , Screen.Register.route)) {
BottomNavigation {
items.forEach { screen ->
BottomNavigationItem(
modifier = Modifier
.background(Color.Gray),
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
Icon(
screen.image , contentDescription = null
)
})
}
} }
}
} }
)*/ }
NavHost(navController, startDestination = "HomeScreen") { ) { innerPadding ->
composable("HomeScreen") { HomeScreen( NavigateToRegister = { navController.navigate("RegisterScreen")} ,NavigateToLogin = { navController.navigate("GameScreen") }) } NavHost(navController, startDestination = Screen.Home.route, Modifier.padding(innerPadding)) {
composable("VocabularyScreen") { VocabularyScreen(onNavigateToList = { navController.popBackStack() }) } composable(Screen.Home.route) { HomeScreen(NavigateToRegister = { navController.navigate(
composable("RegisterScreen") { RegisterScreen(NavigateToApp = { navController.navigate("VocabularyScreen") }) } Screen.Register.route) }, NavigateToLogin = { navController.navigate(Screen.Login.route) }) }
composable("GameScreen"){ GameScreen() { composable(Screen.VocabularyList.route) { VocabularyScreen(onNavigateToList = { navController.popBackStack() }) }
// composable(Screen.Register.route) { RegisterScreen(NavigateToApp = { navController.navigate(
} } Screen.VocabularyList.route) }) }
composable(Screen.Game.route){ GameScreen() {} }
composable(Screen.Login.route){LoginScreen(onLoginClicked = { _, _ -> })}
composable(Screen.Profile.route){profileScreen()}
}
} }
}
@Composable
fun BottomNav(){
} }
@Composable @Composable
@ -89,18 +120,18 @@ fun HomeScreen(NavigateToRegister: () -> Unit,NavigateToLogin: () -> Unit ){
.align(alignment = Alignment.CenterHorizontally)) .align(alignment = Alignment.CenterHorizontally))
Button( onClick = { NavigateToLogin() }, modifier = Modifier Button( onClick = { NavigateToLogin() }, modifier = Modifier
.padding(10.dp) .padding(10.dp)
.width(500.dp) .width(500.dp)
.height(80.dp) .height(80.dp)
.align(alignment = Alignment.CenterHorizontally)) { .align(alignment = Alignment.CenterHorizontally)) {
Text(text = stringResource(id = R.string.logIn), fontSize = 20.sp) Text(text = stringResource(id = R.string.logIn), fontSize = 20.sp)
} }
Button(onClick = { NavigateToRegister() },modifier = Modifier Button(onClick = { NavigateToRegister() },modifier = Modifier
.width(500.dp) .width(500.dp)
.padding(10.dp) .padding(10.dp)
.height(80.dp) .height(80.dp)
.align(alignment = Alignment.CenterHorizontally)) { .align(alignment = Alignment.CenterHorizontally)) {
Text(text = stringResource(id = R.string.signIn), fontSize = 20.sp) Text(text = stringResource(id = R.string.signIn), fontSize = 20.sp)
} }
} }

@ -91,7 +91,7 @@ fun VocCard(vocabulary: Vocabulary){
modifier = Modifier modifier = Modifier
.size(150.dp, 150.dp) .size(150.dp, 150.dp)
.border(2.dp, Color.DarkGray, shape = RoundedCornerShape(8.dp, 8.dp))) { .border(2.dp, Color.DarkGray, shape = RoundedCornerShape(8.dp, 8.dp))) {
Text(vocabulary.name, Text(vocabulary.word,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.border(2.dp, Color.DarkGray, shape = RoundedCornerShape(8.dp, 8.dp)), .border(2.dp, Color.DarkGray, shape = RoundedCornerShape(8.dp, 8.dp)),
@ -104,7 +104,7 @@ fun VocCard(vocabulary: Vocabulary){
.weight(1f) .weight(1f)
.align(Alignment.CenterHorizontally) .align(Alignment.CenterHorizontally)
) )
Text( stringResource(id = R.string.created_by) + (vocabulary.aut ?: stringResource(id = R.string.unknown)), Text( stringResource(id = R.string.created_by) + (vocabulary.word ?: stringResource(id = R.string.unknown)),
modifier = Modifier modifier = Modifier
.wrapContentHeight(Alignment.Bottom) .wrapContentHeight(Alignment.Bottom)
.align(Alignment.End) .align(Alignment.End)
@ -135,12 +135,12 @@ fun VocabularyDetails(vocabulary: Vocabulary){
stickyHeader( stickyHeader(
) { ) {
Text( Text(
vocabulary.name, fontSize = 30.sp, vocabulary.LangueName, fontSize = 30.sp,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Text( Text(
stringResource(R.string.created_by) + (vocabulary.aut stringResource(R.string.created_by) + (vocabulary.word
?: stringResource(id = R.string.unknown)), ?: stringResource(id = R.string.unknown)),
fontSize = 20.sp, fontSize = 20.sp,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,

@ -22,11 +22,15 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import sae.android.sae_2a.R import sae.android.sae_2a.R
import sae.android.sae_2a.VM.LoginViewModel
import sae.android.sae_2a.data.LoginRepository
import sae.android.sae_2a.data.LoginResponseParser
@Composable @Composable
fun LoginScreen(onLoginClicked: (String, String) -> Unit) { fun LoginScreen(onLoginClicked: (String, String) -> Unit) {
var username by remember { mutableStateOf("") } var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") } var password by remember { mutableStateOf("") }
var login = LoginViewModel(LoginRepository(LoginResponseParser())).login("")
Column( Column(
modifier = Modifier modifier = Modifier

@ -6,11 +6,13 @@
<string name="logOut">Log Out</string> <string name="logOut">Log Out</string>
<string name="signIn">Sign In</string> <string name="signIn">Sign In</string>
<string name="password">Password</string> <string name="password">Password</string>
<string name="voc_image_description">Picture representing vocabulary</string> <string name="voc_image_description">Vocabulary</string>
<string name="created_by">Created by\u0020</string> <string name="created_by">Created by\u0020</string>
<string name="unknown">Unknown</string> <string name="unknown">Unknown</string>
<string name="profilePicture">Profile Picture</string> <string name="profilePicture">Profile Picture</string>
<string name="game">Game</string>
<string name="home">Home</string> <string name="home">Home</string>
<string name="register">Register</string>
<string name="accountDetails">Account Details</string> <string name="accountDetails">Account Details</string>
<string name="changepswd">Change Password</string> <string name="changepswd">Change Password</string>
</resources> </resources>
Loading…
Cancel
Save