Lucie GOIGOUX 1 year ago
commit e8bf8156c5

@ -40,7 +40,7 @@ android {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.11"
kotlinCompilerExtensionVersion = "1.5.1"
}
packaging {
resources {
@ -50,6 +50,8 @@ android {
}
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.core:core-ktx:1.12.0")
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
data class Vocabulary(
val name: String,
val aut: String?,
val word : String,
val LangueName: 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.BottomNavigationItem
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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.unit.dp
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.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import sae.android.sae_2a.R
import sae.android.sae_2a.data.Screen
import sae.android.sae_2a.game.VocabularyScreen
@Preview
val items = listOf(
Screen.Game,
Screen.VocabularyList,
Screen.Profile
)
@Preview
@Composable
fun MyApp() {
val navController = rememberNavController()
/* Scaffold(
bottomBar = {
BottomNavigation {
BottomNavigationItem(selected = , onClick = { /*TODO*/ }, icon = { /*TODO*/ }){
Scaffold(
bottomBar = {
val navBackStackEntry by navController.currentBackStackEntryAsState()
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") {
composable("HomeScreen") { HomeScreen( NavigateToRegister = { navController.navigate("RegisterScreen")} ,NavigateToLogin = { navController.navigate("GameScreen") }) }
composable("VocabularyScreen") { VocabularyScreen(onNavigateToList = { navController.popBackStack() }) }
composable("RegisterScreen") { RegisterScreen(NavigateToApp = { navController.navigate("VocabularyScreen") }) }
composable("GameScreen"){ GameScreen() {
//
} }
}
) { innerPadding ->
NavHost(navController, startDestination = Screen.Home.route, Modifier.padding(innerPadding)) {
composable(Screen.Home.route) { HomeScreen(NavigateToRegister = { navController.navigate(
Screen.Register.route) }, NavigateToLogin = { navController.navigate(Screen.Login.route) }) }
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
@ -89,18 +120,18 @@ fun HomeScreen(NavigateToRegister: () -> Unit,NavigateToLogin: () -> Unit ){
.align(alignment = Alignment.CenterHorizontally))
Button( onClick = { NavigateToLogin() }, modifier = Modifier
.padding(10.dp)
.width(500.dp)
.height(80.dp)
.align(alignment = Alignment.CenterHorizontally)) {
.padding(10.dp)
.width(500.dp)
.height(80.dp)
.align(alignment = Alignment.CenterHorizontally)) {
Text(text = stringResource(id = R.string.logIn), fontSize = 20.sp)
}
Button(onClick = { NavigateToRegister() },modifier = Modifier
.width(500.dp)
.padding(10.dp)
.height(80.dp)
.align(alignment = Alignment.CenterHorizontally)) {
.width(500.dp)
.padding(10.dp)
.height(80.dp)
.align(alignment = Alignment.CenterHorizontally)) {
Text(text = stringResource(id = R.string.signIn), fontSize = 20.sp)
}
}

@ -91,7 +91,7 @@ fun VocCard(vocabulary: Vocabulary){
modifier = Modifier
.size(150.dp, 150.dp)
.border(2.dp, Color.DarkGray, shape = RoundedCornerShape(8.dp, 8.dp))) {
Text(vocabulary.name,
Text(vocabulary.word,
modifier = Modifier
.fillMaxWidth()
.border(2.dp, Color.DarkGray, shape = RoundedCornerShape(8.dp, 8.dp)),
@ -104,7 +104,7 @@ fun VocCard(vocabulary: Vocabulary){
.weight(1f)
.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
.wrapContentHeight(Alignment.Bottom)
.align(Alignment.End)
@ -135,12 +135,12 @@ fun VocabularyDetails(vocabulary: Vocabulary){
stickyHeader(
) {
Text(
vocabulary.name, fontSize = 30.sp,
vocabulary.LangueName, fontSize = 30.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
Text(
stringResource(R.string.created_by) + (vocabulary.aut
stringResource(R.string.created_by) + (vocabulary.word
?: stringResource(id = R.string.unknown)),
fontSize = 20.sp,
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.sp
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
fun LoginScreen(onLoginClicked: (String, String) -> Unit) {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var login = LoginViewModel(LoginRepository(LoginResponseParser())).login("")
Column(
modifier = Modifier

@ -6,11 +6,13 @@
<string name="logOut">Log Out</string>
<string name="signIn">Sign In</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="unknown">Unknown</string>
<string name="profilePicture">Profile Picture</string>
<string name="game">Game</string>
<string name="home">Home</string>
<string name="register">Register</string>
<string name="accountDetails">Account Details</string>
<string name="changepswd">Change Password</string>
</resources>
Loading…
Cancel
Save