From b3fa0d740d419b4d9f43f7d735eaf9a5bf59ea44 Mon Sep 17 00:00:00 2001 From: "maxence.guitard" Date: Thu, 14 Mar 2024 19:41:34 +0100 Subject: [PATCH 1/2] feat : button retour et createPlayer (register) --- .../mathseduc/ConnexionPlayerActivity.kt | 272 ++++++++++-------- .../mathseduc/controllers/ControllerPlayer.kt | 33 +++ .../com/example/mathseduc/ui/activity_main.kt | 6 +- 3 files changed, 195 insertions(+), 116 deletions(-) diff --git a/Android/app/src/main/java/com/example/mathseduc/ConnexionPlayerActivity.kt b/Android/app/src/main/java/com/example/mathseduc/ConnexionPlayerActivity.kt index eb9cff8..b27ff25 100644 --- a/Android/app/src/main/java/com/example/mathseduc/ConnexionPlayerActivity.kt +++ b/Android/app/src/main/java/com/example/mathseduc/ConnexionPlayerActivity.kt @@ -5,9 +5,11 @@ import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -28,91 +30,130 @@ class ConnexionPlayerActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { MathsEducTheme { - ConnexionPlayerContent() + ConnexionPlayerContent(activity = this@ConnexionPlayerActivity) } } } } +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun ConnexionPlayerContent() { +fun ConnexionPlayerContent(activity: ConnexionPlayerActivity) { var nickname by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } var showDialog by remember { mutableStateOf(false) } val context = LocalContext.current - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - Image( - painter = painterResource(id = R.drawable.logo), - contentDescription = null, - modifier = Modifier - .size(160.dp, 130.dp) - .background(Color.Gray) // Replace with your background color + Column(modifier = Modifier.fillMaxSize()) { + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.Transparent, + ), + title = {}, + navigationIcon = { + IconButton( + onClick = { activity.finish() }, + modifier = Modifier.size(60.dp) + ) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Retour", + modifier = Modifier.size(36.dp), + tint = Color.White + ) + } + }, ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .weight(1f), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { - Spacer(modifier = Modifier.height(16.dp)) + Image( + painter = painterResource(id = R.drawable.logo), + contentDescription = null, + modifier = Modifier + .size(160.dp, 130.dp) + ) - OutlinedTextField( - value = nickname, - onValueChange = { nickname = it }, - label = { Text("Nickname") }, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) + Spacer(modifier = Modifier.height(16.dp)) - OutlinedTextField( - value = password, - onValueChange = { password = it }, - label = { Text("Password") }, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Password - ), - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) + OutlinedTextField( + value = nickname, + onValueChange = { nickname = it }, + label = { Text("Nickname") }, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White, + disabledContainerColor = Color.White, + focusedBorderColor = Color.Blue, + ), + shape = RoundedCornerShape(8.dp) + ) - Spacer(modifier = Modifier.height(16.dp)) + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Password") }, + visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Password + ), + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White, + disabledContainerColor = Color.White, + focusedBorderColor = Color.Blue, + ), + shape = RoundedCornerShape(8.dp) + ) - Button( - onClick = { - val isAuthenticated = ControllerPlayer.authenticateUser(nickname, password) - if (isAuthenticated != -1) { - MainActivity.idPlayerConnected = isAuthenticated - Toast.makeText(context, "Connexion réussie, bienvenue $nickname !", Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(context, "Connexion échouée. Veuillez réessayer.", Toast.LENGTH_SHORT).show() - } - }, - modifier = Modifier - .fillMaxWidth() - .height(48.dp) - ) { - Text("Login") - } + Spacer(modifier = Modifier.height(16.dp)) - Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { + val isAuthenticated = ControllerPlayer.authenticateUser(nickname, password) + if (isAuthenticated != -1) { + MainActivity.idPlayerConnected = isAuthenticated + Toast.makeText(context, "Connexion réussie, bienvenue $nickname !", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(context, "Connexion échouée. Veuillez réessayer.", Toast.LENGTH_SHORT).show() + } + }, + modifier = Modifier + .fillMaxWidth() + .height(48.dp) + ) { + Text("Login") + } - Button( - onClick = { showDialog = true }, - modifier = Modifier - .fillMaxWidth() - .height(48.dp) - ) { - Text("Register") - } + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = { showDialog = true }, + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color(0xFF40E0D0)) + ) { + Text("Register") + } - if (showDialog) { - RegisterDialog(onDismiss = { showDialog = false }) + if (showDialog) { + RegisterDialog(onDismiss = { showDialog = false }) + } } } } @@ -125,65 +166,66 @@ fun RegisterDialog(onDismiss: () -> Unit) { var password by remember { mutableStateOf("") } AlertDialog( - onDismissRequest = { - onDismiss() - }, - title = { Text("Register") }, - confirmButton = { - Button( - onClick = { - // Save the user information or perform registration - onDismiss() + onDismissRequest = { + onDismiss() + }, + title = { Text("Register") }, + confirmButton = { + Button( + onClick = { + val playerId = ControllerPlayer.createPlayer(nickname, password) + onDismiss() + } + ) { + Text("Save") } - ) { - Text("Save") - } - }, - dismissButton = { - Button( - onClick = { - onDismiss() + }, + dismissButton = { + Button( + onClick = { + onDismiss() + } + ) { + Text("Dismiss") + } + }, + text = { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) { + OutlinedTextField( + value = nickname, + onValueChange = { nickname = it }, + label = { Text("Nickname") }, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) + + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Password") }, + visualTransformation = PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Password + ), + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) } - ) { - Text("Dismiss") - } - }, - text = { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) { - OutlinedTextField( - value = nickname, - onValueChange = { nickname = it }, - label = { Text("Nickname") }, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) - - OutlinedTextField( - value = password, - onValueChange = { password = it }, - label = { Text("Password") }, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Password - ), - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) } - } ) } @Preview(showBackground = true) @Composable fun ConnexionPlayerPreview() { + val fakeActivity = ConnexionPlayerActivity() MathsEducTheme { - ConnexionPlayerContent() + ConnexionPlayerContent(activity = fakeActivity) } } \ No newline at end of file diff --git a/Android/app/src/main/java/com/example/mathseduc/controllers/ControllerPlayer.kt b/Android/app/src/main/java/com/example/mathseduc/controllers/ControllerPlayer.kt index 57c41a0..22a99d9 100644 --- a/Android/app/src/main/java/com/example/mathseduc/controllers/ControllerPlayer.kt +++ b/Android/app/src/main/java/com/example/mathseduc/controllers/ControllerPlayer.kt @@ -62,6 +62,39 @@ class ControllerPlayer { return null } + + fun createPlayer(nickname: String, password: String): Int { + val policy = StrictMode.ThreadPolicy.Builder().permitAll().build() + StrictMode.setThreadPolicy(policy) + + val client = OkHttpClient() + + val hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt()) + + val requestBody = FormBody.Builder() + .add("nickname", nickname) + .add("password", hashedPassword) + .build() + + val request = Request.Builder() + .url("https://trusting-panini.87-106-126-109.plesk.page/api/add/player/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO") + .post(requestBody) + .build() + + client.newCall(request).execute().use { response -> + if (response.isSuccessful) { + val responseData = response.body?.string() + + val gson = Gson() + val player: Player = gson.fromJson(responseData, Player::class.java) + + return player.id + } else { + throw IOException("Failed to create player: ${response.code}") + } + } + } + fun authenticateUser(nickname: String, password: String): Int { val policy = StrictMode.ThreadPolicy.Builder().permitAll().build() diff --git a/Android/app/src/main/java/com/example/mathseduc/ui/activity_main.kt b/Android/app/src/main/java/com/example/mathseduc/ui/activity_main.kt index 26e5f5f..4e3fd8f 100644 --- a/Android/app/src/main/java/com/example/mathseduc/ui/activity_main.kt +++ b/Android/app/src/main/java/com/example/mathseduc/ui/activity_main.kt @@ -1,6 +1,7 @@ package com.example.mathseduc.ui import android.content.Intent +import android.widget.Toast import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -23,6 +24,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import com.example.mathseduc.ConnexionPlayerActivity +import com.example.mathseduc.MainActivity import com.example.mathseduc.MultiActivity import com.example.mathseduc.R import com.example.mathseduc.ui.theme.Colors @@ -96,7 +99,8 @@ fun HomePage() { Spacer(modifier = Modifier.height(16.dp)) Button( - onClick = { /* Handle connexion button click */ }, + onClick = { val intent = Intent(context, ConnexionPlayerActivity::class.java) + context.startActivity(intent) }, shape = RoundedCornerShape(15), colors = ButtonDefaults.buttonColors(Colors.Grey), modifier = Modifier From 7416798f95c06706a4ca20d900989d22ab0755dd Mon Sep 17 00:00:00 2001 From: Yvan Date: Thu, 14 Mar 2024 19:51:21 +0100 Subject: [PATCH 2/2] fix : reduction lags using coroutines --- .../example/mathseduc/ui/activity_multi.kt | 100 +++++++++++------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/Android/app/src/main/java/com/example/mathseduc/ui/activity_multi.kt b/Android/app/src/main/java/com/example/mathseduc/ui/activity_multi.kt index 7231802..2dce33a 100644 --- a/Android/app/src/main/java/com/example/mathseduc/ui/activity_multi.kt +++ b/Android/app/src/main/java/com/example/mathseduc/ui/activity_multi.kt @@ -1,7 +1,7 @@ package com.example.mathseduc.ui + +import android.content.Context import android.content.Intent -import android.os.Handler -import android.os.Looper import android.util.Log import android.widget.Toast import androidx.compose.foundation.background @@ -26,6 +26,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -42,37 +44,47 @@ import com.example.mathseduc.controllers.ControllerLobby import com.example.mathseduc.controllers.ControllerUtiliser import com.example.mathseduc.models.Lobby import com.example.mathseduc.ui.theme.Colors +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import okhttp3.MultipartBody - @Composable fun MultiPage() { val context = LocalContext.current var selectedItem by rememberSaveable { mutableStateOf(null) } - - var lobbyList by rememberSaveable { mutableStateOf(ControllerLobby.getLobbies() ?: emptyList()) } - var refreshState by rememberSaveable { mutableStateOf(true) } + var lobbyList by remember { mutableStateOf>(emptyList()) } + val scope = rememberCoroutineScope() + var refreshState by remember { mutableStateOf(true) } + + // Fonction pour actualiser la liste des lobbies + suspend fun refreshLobbyList() { + val refreshedLobbyList = withContext(Dispatchers.IO) { + ControllerLobby.getLobbies() ?: emptyList() + } + lobbyList = refreshedLobbyList + Log.e("MainActivity", "Refreshed Lobby List") + } DisposableEffect(refreshState) { - val handler = Handler(Looper.getMainLooper()) - val refreshRunnable = object : Runnable { - override fun run() { - lobbyList = emptyList() - lobbyList = ControllerLobby.getLobbies()!! - selectedItem = null - if (refreshState){ - handler.postDelayed(this,3000) - Log.e("MainActivity", "Refresh Multi") + val refreshRunnable = { + scope.launch(Dispatchers.IO) { + try { + while (refreshState) { + delay(5000) + refreshLobbyList() + } + } catch (e: Exception) { + Log.e("MainActivity", "Error refreshing lobbies: ${e.message}") } } } - - handler.post(refreshRunnable) + refreshRunnable.invoke() onDispose { Toast.makeText(context, "Au REVOIR UwU", Toast.LENGTH_SHORT).show() refreshState = false - handler.removeCallbacks(refreshRunnable) } } @@ -117,7 +129,6 @@ fun MultiPage() { Button( onClick = { refreshState = false - val intent = Intent(context, CreateLobbyActivity::class.java) context.startActivity(intent) }, @@ -136,26 +147,23 @@ fun MultiPage() { .fillMaxSize() .weight(0.85f) ) { - items(lobbyList ?: emptyList()) { lobby -> + items(lobbyList) { lobby -> LobbyItem(lobby, selectedItem == lobby) { selectedItem = it - refreshState = false - if (ControllerLobby.getNbPlayerInLobby(selectedItem!!.id) < selectedItem!!.nbplayers){ - val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM) - formDataBuilder.addFormDataPart("idplayer", MainActivity.idPlayerConnected.toString()) - formDataBuilder.addFormDataPart("idlobby", selectedItem!!.id.toString()) - formDataBuilder.addFormDataPart("playertime", "0") - - ControllerUtiliser.createUtiliserByIdLobby(formDataBuilder) - - val intent = Intent(context, ServerDetailsActivity::class.java) - intent.putExtra("serverName", selectedItem!!.name) - intent.putExtra("lobbyId", selectedItem!!.id) - context.startActivity(intent) + if (ControllerLobby.getNbPlayerInLobby(selectedItem!!.id) < selectedItem!!.nbplayers) { + // Créer un Utiliser si le lobby n'est pas plein + scope.launch { + createUtiliserForLobby(context, selectedItem!!) + } } else { - Toast.makeText(context, "Oh nan, le serveur est déjà plein ! Réessayer plus tard.", Toast.LENGTH_SHORT).show() + // Afficher un message Toast si le lobby est plein + Toast.makeText( + context, + "Oh nan, le serveur est déjà plein ! Réessayer plus tard.", + Toast.LENGTH_SHORT + ).show() } } } @@ -164,10 +172,26 @@ fun MultiPage() { } } +// Fonction pour créer un Utiliser pour le lobby sélectionné +private suspend fun createUtiliserForLobby(context: Context, lobby: Lobby) { + withContext(Dispatchers.IO) { + val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM) + formDataBuilder.addFormDataPart("idplayer", MainActivity.idPlayerConnected.toString()) + formDataBuilder.addFormDataPart("idlobby", lobby.id.toString()) + formDataBuilder.addFormDataPart("playertime", "0") + + ControllerUtiliser.createUtiliserByIdLobby(formDataBuilder) + + // Naviguer vers l'activité ServerDetails avec les détails du lobby + val intent = Intent(context, ServerDetailsActivity::class.java) + intent.putExtra("serverName", lobby.name) + intent.putExtra("lobbyId", lobby.id) + context.startActivity(intent) + } +} + @Composable fun LobbyItem(lobby: Lobby, isSelected: Boolean, onItemClick: (Lobby) -> Unit) { - val nbCurrentPlayer = ControllerLobby.getNbPlayerInLobby(lobby.id) - Row( modifier = Modifier .fillMaxWidth() @@ -184,7 +208,6 @@ fun LobbyItem(lobby: Lobby, isSelected: Boolean, onItemClick: (Lobby) -> Unit) { fontSize = 18.sp, color = Colors.White, modifier = Modifier.weight(1f) - ) Text( text = "${ControllerLobby.getNbPlayerInLobby(lobby.id)}/${lobby.nbplayers}", @@ -205,5 +228,4 @@ fun LobbyItem(lobby: Lobby, isSelected: Boolean, onItemClick: (Lobby) -> Unit) { modifier = Modifier.weight(1f) ) } -} - +} \ No newline at end of file