diff --git a/Android/app/build.gradle.kts b/Android/app/build.gradle.kts index 3aa618f..6b5e23b 100644 --- a/Android/app/build.gradle.kts +++ b/Android/app/build.gradle.kts @@ -62,6 +62,8 @@ dependencies { implementation("androidx.compose.ui:ui-graphics-android:1.6.3") implementation("androidx.navigation:navigation-common-ktx:2.7.7") implementation("androidx.navigation:navigation-compose:2.7.7") + implementation("androidx.compose.material:material-icons-core") + implementation("androidx.compose.material:material-icons-extended") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/Android/app/src/main/java/com/example/mathseduc/AppNavigation.kt b/Android/app/src/main/java/com/example/mathseduc/AppNavigation.kt index 8765f97..08c2206 100644 --- a/Android/app/src/main/java/com/example/mathseduc/AppNavigation.kt +++ b/Android/app/src/main/java/com/example/mathseduc/AppNavigation.kt @@ -7,6 +7,7 @@ import androidx.navigation.compose.rememberNavController import com.example.mathseduc.ui.CreateLobbyPage import com.example.mathseduc.ui.HomePage import com.example.mathseduc.ui.MultiPage +import com.example.mathseduc.ui.ProfilePlayerPage @Composable fun AppNavigation() { @@ -17,6 +18,7 @@ fun AppNavigation() { composable("connexion") { ConnexionPlayerContent(navController) } composable("multiplayer") { MultiPage(navController) } composable("createLobby") { CreateLobbyPage(navController) } + composable("profilePlayer") { ProfilePlayerPage(navController) } //composable("serverDetails/{serverName}/{lobbyId}") { ServerDetailPage(navController) } } } \ 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 347a558..12ce4e5 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 @@ -1,12 +1,14 @@ package com.example.mathseduc.controllers import android.os.StrictMode +import android.util.Log import com.example.mathseduc.models.* import com.google.gson.Gson import com.google.gson.reflect.TypeToken import okhttp3.FormBody import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.Response import org.mindrot.jbcrypt.BCrypt import java.io.IOException @@ -94,6 +96,34 @@ class ControllerPlayer { } } } + /* + fun updatePlayer(playerId: String,nickname: String, password: String) { + try { + 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 updateRequest = Request.Builder() + .url("https://trusting-panini.87-106-126-109.plesk.page/api/update/player/${playerId}/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO") + .post(requestBody) + .build() + + val updateResponse: Response = client.newCall(updateRequest).execute() + if (!updateResponse.isSuccessful) { + Log.e("updatePlayer", "Error updating player") + } + } catch (e: Exception) { + Log.e("updateLobby", "Error updating lobby", e) + } + }*/ fun authenticateUser(nickname: String, password: String): Int { diff --git a/Android/app/src/main/java/com/example/mathseduc/ui/activity_create_lobby.kt b/Android/app/src/main/java/com/example/mathseduc/ui/activity_create_lobby.kt index 1d85b70..c91f840 100644 --- a/Android/app/src/main/java/com/example/mathseduc/ui/activity_create_lobby.kt +++ b/Android/app/src/main/java/com/example/mathseduc/ui/activity_create_lobby.kt @@ -32,10 +32,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue 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.onGloballyPositioned import androidx.compose.ui.platform.LocalConfiguration @@ -45,7 +45,6 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -64,11 +63,11 @@ import okhttp3.MultipartBody @Composable fun CreateLobbyPage(navController: NavController) { - var lobbyName by remember { mutableStateOf("") } - var password by remember { mutableStateOf("") } - var nbPlayers by remember { mutableStateOf("") } - var difficulty by remember { mutableStateOf("") } - var chapter by remember { mutableStateOf("") } + var lobbyName by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + var nbPlayers by rememberSaveable { mutableStateOf("") } + var difficulty by rememberSaveable { mutableStateOf("") } + var chapter by rememberSaveable { mutableStateOf("") } var expandedDifficulty by remember { mutableStateOf(false) } var expandedChapter by remember { mutableStateOf(false) } var size by remember { mutableStateOf(IntSize.Zero) } @@ -91,7 +90,7 @@ fun CreateLobbyPage(navController: NavController) { Column( modifier = Modifier .fillMaxSize() - .padding(if(isPortrait)16.dp else 5.dp), + .padding(if (isPortrait) 16.dp else 5.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -112,7 +111,7 @@ fun CreateLobbyPage(navController: NavController) { onValueChange = { lobbyName = it }, modifier = Modifier .fillMaxWidth(if (isPortrait) 1f else 0.5f) - .padding(if(isPortrait) 8.dp else 4.dp), + .padding(if (isPortrait) 8.dp else 4.dp), shape = RoundedCornerShape(8.dp), label = { Text("Lobby Name") } ) @@ -123,7 +122,7 @@ fun CreateLobbyPage(navController: NavController) { onValueChange = { password = it }, modifier = Modifier .fillMaxWidth(if (isPortrait) 1f else 0.5f) - .padding(if(isPortrait) 8.dp else 4.dp), + .padding(if (isPortrait) 8.dp else 4.dp), shape = RoundedCornerShape(8.dp), label = { Text("Password") }, visualTransformation = PasswordVisualTransformation(), @@ -136,7 +135,7 @@ fun CreateLobbyPage(navController: NavController) { onValueChange = { nbPlayers = it }, modifier = Modifier .fillMaxWidth(if (isPortrait) 1f else 0.5f) - .padding(if(isPortrait) 8.dp else 4.dp), + .padding(if (isPortrait) 8.dp else 4.dp), label = { Text("Number of Players") }, keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number) ) @@ -162,7 +161,7 @@ fun CreateLobbyPage(navController: NavController) { size = it.size } .fillMaxWidth(if (isPortrait) 1f else 0.5f) - .padding(if(isPortrait) 8.dp else 4.dp), + .padding(if (isPortrait) 8.dp else 4.dp), label = { Text("Difficulté") }, trailingIcon = { Icon( @@ -179,7 +178,7 @@ fun CreateLobbyPage(navController: NavController) { onDismissRequest = { expandedDifficulty = false }, modifier = Modifier .width(with(LocalDensity.current) { size.width.toDp() }) - .padding(if(isPortrait) 8.dp else 4.dp) + .padding(if (isPortrait) 8.dp else 4.dp) ) { difficultyOptions.forEach { option -> DropdownMenuItem( @@ -216,7 +215,7 @@ fun CreateLobbyPage(navController: NavController) { size = it.size } .fillMaxWidth(if (isPortrait) 1f else 0.5f) - .padding(if(isPortrait) 8.dp else 4.dp), + .padding(if (isPortrait) 8.dp else 4.dp), label = { Text("Chapitre") }, trailingIcon = { Icon( @@ -233,7 +232,7 @@ fun CreateLobbyPage(navController: NavController) { onDismissRequest = { expandedChapter = false }, modifier = Modifier .width(with(LocalDensity.current) { size.width.toDp() }) - .padding(if(isPortrait) 8.dp else 4.dp) + .padding(if (isPortrait) 8.dp else 4.dp) ) { chapterOptions?.forEach { option -> @@ -305,7 +304,7 @@ fun CreateLobbyPage(navController: NavController) { }, modifier = Modifier .fillMaxWidth(if (isPortrait) 1f else 0.5f) - .padding(if(isPortrait) 8.dp else 4.dp) + .padding(if (isPortrait) 8.dp else 4.dp) ) { Text(text = "Create Lobby", color = Colors.White) } 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 a79c961..127e614 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 @@ -122,21 +122,42 @@ fun HomePage(navController: NavController) { Spacer(modifier = Modifier.height(16.dp)) - Button( - onClick = { - navController.navigate("connexion") - }, - shape = RoundedCornerShape(15), - colors = ButtonDefaults.buttonColors(Colors.Grey), - modifier = Modifier - .fillMaxWidth(if (isPortrait) 1f else 0.5f) - .height(48.dp) - .padding(horizontal = 16.dp) - ) { - Text( - text = "Connexion", - color = Color.White - ) + if(MainActivity.idPlayerConnected != -1){ + Button( + onClick = { + navController.navigate("profilePlayer") + }, + shape = RoundedCornerShape(15), + colors = ButtonDefaults.buttonColors(Colors.Grey), + modifier = Modifier + .fillMaxWidth(if (isPortrait) 1f else 0.5f) + .height(48.dp) + .padding(horizontal = 16.dp) + ) { + Text( + text = "Profile", + color = Color.White + ) + } + } + else { + Button( + onClick = { + navController.navigate("connexion") + }, + shape = RoundedCornerShape(15), + colors = ButtonDefaults.buttonColors(Colors.Grey), + modifier = Modifier + .fillMaxWidth(if (isPortrait) 1f else 0.5f) + .height(48.dp) + .padding(horizontal = 16.dp) + ) { + Text( + text = "Connexion", + color = Color.White + ) + } } + } } \ No newline at end of file diff --git a/Android/app/src/main/java/com/example/mathseduc/ui/activity_profil_player.kt b/Android/app/src/main/java/com/example/mathseduc/ui/activity_profil_player.kt index a9f6aa3..7df0f3a 100644 --- a/Android/app/src/main/java/com/example/mathseduc/ui/activity_profil_player.kt +++ b/Android/app/src/main/java/com/example/mathseduc/ui/activity_profil_player.kt @@ -1,2 +1,243 @@ package com.example.mathseduc.ui +import android.app.Activity +import android.content.res.Configuration +import android.widget.Toast +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +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.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.view.WindowCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat +import androidx.navigation.NavController +import com.example.mathseduc.MainActivity +import com.example.mathseduc.RegisterDialog +import com.example.mathseduc.controllers.ControllerPlayer +import com.example.mathseduc.ui.theme.Colors + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ProfilePlayerPage(navController: NavController) { + val activity = LocalView.current.context as Activity + val player = ControllerPlayer.getPlayerInfoById(MainActivity.idPlayerConnected.toString()) + + var showDialog by rememberSaveable { mutableStateOf(false) } + + val isPortrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT + + WindowCompat.setDecorFitsSystemWindows(activity.window, false) + + val windowInsetsController = remember { + WindowCompat.getInsetsController(activity.window, activity.window.decorView) + } + + windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + + // Hide the status bar and navigation bar + windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) + + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.Transparent, + ), + title = {}, + navigationIcon = { + IconButton( + onClick = { navController.navigate("home") }, + modifier = Modifier.size(60.dp) + ) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Retour", + modifier = Modifier.size(36.dp), + tint = Color.White + ) + } + }, + ) + + Column( + modifier = Modifier + .fillMaxSize() + .padding(14.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + modifier = Modifier + .padding(5.dp) + .fillMaxWidth(if (isPortrait) 1f else 0.5f) + .background(Color.White, RoundedCornerShape(16.dp)) + .padding(vertical = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Profile", + fontSize = 25.sp, + color = Color.Black, + fontWeight = FontWeight.Bold + ) + + Spacer(modifier = Modifier.height(10.dp)) + + Divider( + color = Color.Gray, + thickness = 2.dp, + modifier = Modifier.padding(horizontal = 10.dp) + ) + + Spacer(modifier = Modifier.height(10.dp)) + + if (player != null) { + Text( + text = "Nickname : ${player.nickname}", + fontSize = 19.sp, + color = Colors.Black + ) + } + Spacer(modifier = Modifier.height(20.dp)) + + Button( + onClick = { showDialog = true }, + shape = RoundedCornerShape(15), + colors = ButtonDefaults.buttonColors(Colors.Blue), + modifier = Modifier + .height(48.dp) + .padding(horizontal = 6.dp) + ) { + Text( + text = "Change Information", + color = Color.White + ) + } + + Spacer(modifier = Modifier.height(10.dp)) + Button( + onClick = { }, + shape = RoundedCornerShape(15), + colors = ButtonDefaults.buttonColors(Colors.Red), + modifier = Modifier + .height(48.dp) + .padding(horizontal = 16.dp) + ) { + Text( + text = "Delete Account", + color = Color.White + ) + } + if (showDialog) { + if (player != null) { + ChangeDialog(player.id.toString(),onDismiss = { showDialog = false }) + } + } + } +} +} + +@Composable +fun ChangeDialog(playerId: String,onDismiss: () -> Unit) { + val context = LocalContext.current + + var nickname by rememberSaveable { mutableStateOf("") } + var password by rememberSaveable { mutableStateOf("") } + + AlertDialog( + onDismissRequest = { + onDismiss() + }, + title = { Text("Change Information") }, + confirmButton = { + Button( + colors = ButtonDefaults.buttonColors(Colors.Cyan), + onClick = { + if (nickname.isNotBlank() && password.isNotBlank()) { + /*ControllerPlayer.updatePlayer(playerId,nickname,password)*/ + onDismiss() + } else { + Toast.makeText(context, "Please fill in all fields", Toast.LENGTH_SHORT).show() + } + } + ) { + Text("Save") + } + }, + dismissButton = { + Button( + colors = ButtonDefaults.buttonColors(Colors.BlackGrey), + 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) + ) + } + } + ) +}