Compare commits
343 Commits
@ -1,17 +1,61 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
|
||||
name: default
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
|
||||
steps:
|
||||
- name: sonar
|
||||
image: sonarsource/sonar-scanner-cli:5
|
||||
- name: build
|
||||
image: mcr.microsoft.com/dotnet/sdk:7.0
|
||||
commands:
|
||||
- cd Blazor/
|
||||
- dotnet restore Blazor.sln
|
||||
- dotnet build Blazor.sln -c Release --no-restore
|
||||
|
||||
- name: tests
|
||||
image: mcr.microsoft.com/dotnet/sdk:7.0
|
||||
commands:
|
||||
- cd Blazor/
|
||||
- dotnet restore Blazor.sln
|
||||
- dotnet test Blazor.sln --no-restore
|
||||
depends_on: [build]
|
||||
|
||||
- name: code-analysis
|
||||
image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dronesonarplugin-dotnet7
|
||||
commands:
|
||||
- sonar-scanner -Dsonar.projectKey=MuscuMaths -Dsonar.sources=. -Dsonar.login=$${PLUGIN_SONAR_TOKEN} -Dsonar.host.url=$${PLUGIN_SONAR_HOST} -Dsonar.php.coverage.reportPaths=coverage.xml
|
||||
- cd Blazor/
|
||||
- dotnet restore Blazor.sln
|
||||
- dotnet sonarscanner begin /k:MuscuMaths /d:sonar.host.url=$${PLUGIN_SONAR_HOST} /d:sonar.login=$${PLUGIN_SONAR_TOKEN} /d:sonar.coverageReportPaths="coveragereport/SonarQube.xml"
|
||||
- dotnet sonarscanner begin /k:MuscuMaths /d:sonar.host.url=$${PLUGIN_SONAR_HOST} /d:sonar.login=$${PLUGIN_SONAR_TOKEN}
|
||||
- dotnet build Blazor.sln -c Release --no-restore
|
||||
- dotnet test Blazor.sln --logger trx --no-restore /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura --collect "XPlat Code Coverage"
|
||||
- reportgenerator -reports:"**/coverage.cobertura.xml" -reporttypes:SonarQube -targetdir:"coveragereport" -verbosity:Verbose
|
||||
- dotnet publish Blazor.sln -c Release --no-restore -o $CI_PROJECT_DIR/build/release
|
||||
- dotnet sonarscanner end /d:sonar.login=$${PLUGIN_SONAR_TOKEN}
|
||||
secrets: [ PLUGIN_SONAR_TOKEN ]
|
||||
settings:
|
||||
sonar_host:
|
||||
from_secret: sonar_host
|
||||
# accessible en ligne de commande par $${PLUGIN_SONAR_HOST}
|
||||
sonar_host: https://codefirst.iut.uca.fr/sonar/
|
||||
# accessible en ligne de commande par $${PLUGIN_SONAR_TOKEN}
|
||||
sonar_token:
|
||||
from_secret: sonar_token
|
||||
from_secret: PLUGIN_SONAR_TOKEN
|
||||
depends_on: [tests]
|
||||
|
||||
- name: generate-and-deploy-docs
|
||||
image: hub.codefirst.iut.uca.fr/marc.chevaldonne/codefirst-dronesonarplugin-dotnet7
|
||||
failure: ignore
|
||||
volumes:
|
||||
- name: docs
|
||||
path: /docs
|
||||
commands:
|
||||
- /entrypoint.sh
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
depends_on: [ build ]
|
||||
|
||||
volumes:
|
||||
- name: docs
|
||||
temp: {}
|
@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,80 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.mathseduc"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.mathseduc"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||
implementation("androidx.activity:activity-compose:1.7.0")
|
||||
implementation(platform("androidx.compose:compose-bom:2023.08.00"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
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")
|
||||
implementation("androidx.navigation:navigation-runtime-ktx:2.7.7")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
|
||||
implementation("com.squareup.okhttp3:okhttp:4.10.0")
|
||||
implementation("com.google.code.gson:gson:2.10.1")
|
||||
implementation("org.mindrot:jbcrypt:0.4")
|
||||
implementation("androidx.navigation:navigation-compose:2.7.7")
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,24 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.example.mathseduc", appContext.packageName)
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.MathsEduc"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.MathsEduc">
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".MultiActivity" />
|
||||
<activity android:name=".ConnexionPlayerActivity" />
|
||||
<activity android:name=".CreateLobbyActivity" />
|
||||
<activity android:name=".ServerDetailsActivity" />
|
||||
<activity android:name=".QuizMultiActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,57 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
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.mathseduc.ui.CreateLobbyPage
|
||||
import com.example.mathseduc.ui.HomePage
|
||||
import com.example.mathseduc.ui.MultiPage
|
||||
import com.example.mathseduc.ui.QuizMultiScreen
|
||||
import com.example.mathseduc.ui.ServerDetailPage
|
||||
import com.example.mathseduc.ui.ProfilePlayerPage
|
||||
import com.example.mathseduc.ui.ThemeChoicePage
|
||||
|
||||
@Composable
|
||||
fun AppNavigation() {
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(navController = navController, startDestination = "home") {
|
||||
composable("home") { HomePage(navController) }
|
||||
composable("connexion") { ConnexionPlayerContent(navController) }
|
||||
composable("multiplayer") { MultiPage(navController) }
|
||||
composable("createLobby") { CreateLobbyPage(navController) }
|
||||
composable("profilePlayer") { ProfilePlayerPage(navController) }
|
||||
composable("themeChoice") { ThemeChoicePage(navController) }
|
||||
//composable("serverDetails/{serverName}/{lobbyId}") { ServerDetailPage(navController) }
|
||||
composable(
|
||||
route = "serverDetails/{lobbyName}/{lobbyId}/{lobbyChapter}/{lobbyNbPlayers}/{lobbyDifficulty}",
|
||||
arguments = listOf(
|
||||
navArgument("lobbyName") { type = NavType.StringType },
|
||||
navArgument("lobbyId") { type = NavType.IntType },
|
||||
navArgument("lobbyChapter") { type = NavType.IntType },
|
||||
navArgument("lobbyNbPlayers") { type = NavType.IntType },
|
||||
navArgument("lobbyDifficulty") { type = NavType.IntType }
|
||||
)
|
||||
) { backStackEntry ->
|
||||
val lobbyName = backStackEntry.arguments?.getString("lobbyName")
|
||||
val lobbyId = backStackEntry.arguments?.getInt("lobbyId")
|
||||
val lobbyChapter = backStackEntry.arguments?.getInt("lobbyChapter")
|
||||
val lobbyNbPlayers = backStackEntry.arguments?.getInt("lobbyNbPlayers")
|
||||
val lobbyDifficulty = backStackEntry.arguments?.getInt("lobbyDifficulty")
|
||||
|
||||
ServerDetailPage(navController, lobbyName,lobbyId, lobbyChapter, lobbyNbPlayers, lobbyDifficulty)
|
||||
}
|
||||
composable(
|
||||
route = "quizMultiScreen/{lobbyId}",
|
||||
arguments = listOf(
|
||||
navArgument("lobbyId") { type = NavType.IntType }
|
||||
)
|
||||
) { backStackEntry ->
|
||||
val lobbyId = backStackEntry.arguments?.getInt("lobbyId")
|
||||
QuizMultiScreen(navController, lobbyId)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
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.runtime.saveable.rememberSaveable
|
||||
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.res.painterResource
|
||||
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.dp
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.mathseduc.controllers.ControllerPlayer
|
||||
|
||||
|
||||
class ConnexionPlayerActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
ConnexionPlayerContent(navController = navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ConnexionPlayerContent(navController: NavController) {
|
||||
var nickname by rememberSaveable { mutableStateOf("") }
|
||||
var password by rememberSaveable { mutableStateOf("") }
|
||||
var showDialog by rememberSaveable { mutableStateOf(false) }
|
||||
val activity = LocalView.current.context as Activity
|
||||
|
||||
val context = LocalContext.current
|
||||
val isPortrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(activity.window, false)
|
||||
|
||||
val windowInsetsController = remember {
|
||||
WindowCompat.getInsetsController(activity.window, activity.window.decorView)
|
||||
}
|
||||
|
||||
// Hide the status bar
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.statusBars())
|
||||
|
||||
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(16.dp,0.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.logo),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(0.dp, 2.dp, 0.dp)
|
||||
.size(if (isPortrait) 120.dp else 90.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(if (isPortrait) 16.dp else 4.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
value = nickname,
|
||||
onValueChange = { nickname = it },
|
||||
label = { Text("Nickname") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.padding(if (isPortrait) 8.dp else 8.dp,3.dp),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedContainerColor = Color.White,
|
||||
unfocusedContainerColor = Color.White,
|
||||
disabledContainerColor = Color.White,
|
||||
focusedBorderColor = Color.Blue,
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
label = { Text("Password") },
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Password
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.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(if (isPortrait) 16.dp else 7.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()
|
||||
navController.navigate("home")
|
||||
} else {
|
||||
Toast.makeText(context, "Connexion échouée. Veuillez réessayer.", Toast.LENGTH_SHORT).show()
|
||||
nickname = ""
|
||||
password = ""
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.width(230.dp)
|
||||
.height(48.dp)
|
||||
) {
|
||||
Text("Login")
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(if (isPortrait) 16.dp else 10.dp))
|
||||
|
||||
Button(
|
||||
onClick = { showDialog = true },
|
||||
modifier = Modifier
|
||||
.width(230.dp)
|
||||
.height(48.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF40E0D0))
|
||||
) {
|
||||
Text("Register")
|
||||
}
|
||||
|
||||
if (showDialog) {
|
||||
RegisterDialog(onDismiss = { showDialog = false })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun RegisterDialog(onDismiss: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
var nickname by remember { mutableStateOf("") }
|
||||
var password by remember { mutableStateOf("") }
|
||||
|
||||
var nicknameError by remember { mutableStateOf(false) }
|
||||
var passwordError by remember { mutableStateOf(false) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
onDismiss()
|
||||
},
|
||||
title = { Text("Register") },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
if (nickname.isNotBlank() && password.isNotBlank()) {
|
||||
val playerId = ControllerPlayer.createPlayer(nickname, password)
|
||||
onDismiss()
|
||||
} else {
|
||||
if (nickname.isBlank()) nicknameError = true
|
||||
if (password.isBlank()) passwordError = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text("Save")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
}
|
||||
) {
|
||||
Text("Dismiss")
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = nickname,
|
||||
onValueChange = {
|
||||
nickname = it
|
||||
nicknameError = false
|
||||
},
|
||||
label = { Text("Nickname") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
isError = nicknameError
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = {
|
||||
password = it
|
||||
passwordError = false
|
||||
},
|
||||
label = { Text("Password") },
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Password
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
isError = passwordError
|
||||
)
|
||||
|
||||
if (nicknameError || passwordError) {
|
||||
Text(
|
||||
text = "Please fill in all fields",
|
||||
color = Color.Red,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.mathseduc.ui.CreateLobbyPage
|
||||
|
||||
class CreateLobbyActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
CreateLobbyPage(navController = navController)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.example.mathseduc.ui.HomePage
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
companion object {
|
||||
var idPlayerConnected: Int = -1
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
AppNavigation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MainActivityPreview() {
|
||||
MainActivity()
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.mathseduc.ui.MultiPage
|
||||
|
||||
|
||||
class MultiActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
MultiPage(navController)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import com.example.mathseduc.ui.theme.MathsEducTheme
|
||||
|
||||
|
||||
class QuizMultiActivity : ComponentActivity() {
|
||||
companion object {
|
||||
var countDownTimer: CountDownTimer? = null
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
MathsEducTheme {
|
||||
/*
|
||||
val lobbyId = intent.getIntExtra("lobbyId",-1)
|
||||
val serverName = intent.getStringExtra("serverName")
|
||||
|
||||
QuizMultiScreen(lobbyId, serverName!!) //TODO sus
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
// Stop the CountDownTimer to avoid memory leaks
|
||||
countDownTimer?.cancel()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.compose.setContent
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
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.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.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerUtiliser
|
||||
import com.example.mathseduc.models.Player
|
||||
import com.example.mathseduc.ui.theme.Colors
|
||||
import com.example.mathseduc.viewModel.ServerDetailsViewModel
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class ServerDetailsActivity : ComponentActivity() {
|
||||
|
||||
private var playerList: List<Player> = emptyList()
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val refreshInterval: Long = 2000
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
myBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
//ServerDetailPage(navController = navController)
|
||||
}
|
||||
}
|
||||
|
||||
private fun myBackPressed() {
|
||||
val lobbyId = intent.getIntExtra("lobbyId", -1)
|
||||
|
||||
ControllerUtiliser.DeleteUtiliserForLobby(MainActivity.idPlayerConnected, lobbyId)
|
||||
|
||||
if (ControllerLobby.playerCreatorIdPresentInLobby(MainActivity.idPlayerConnected, lobbyId)) {
|
||||
val idNextPlayerCreator = ControllerUtiliser.getIdNextPlayerInLobby(lobbyId)
|
||||
|
||||
if (idNextPlayerCreator == -1) {
|
||||
ControllerLobby.deleteLobby(lobbyId)
|
||||
} else {
|
||||
val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
formDataBuilder.addFormDataPart("idplayercreator", idNextPlayerCreator.toString())
|
||||
ControllerLobby.updateLobbyIdCreatorLobby(lobbyId, formDataBuilder)
|
||||
}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.example.mathseduc.controllers
|
||||
|
||||
import android.os.StrictMode
|
||||
import com.example.mathseduc.models.Chapter
|
||||
import com.example.mathseduc.models.Lobby
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.IOException
|
||||
|
||||
class ControllerChapter {
|
||||
companion object {
|
||||
fun getChapters(): ArrayList<Chapter>? {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/all/chapters/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO/1")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Chapter>>() {}.type
|
||||
|
||||
// Parse API response and return the list of chapters
|
||||
return gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun getChapterNameById(idchapter : Int?): String? {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/chapters/$idchapter/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Chapter>>() {}.type
|
||||
|
||||
// Parse API response and return the list of chapters
|
||||
val chapter: ArrayList<Chapter> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
return chapter[0].name
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,356 @@
|
||||
package com.example.mathseduc.controllers
|
||||
|
||||
import android.os.StrictMode
|
||||
import android.util.Log
|
||||
import com.example.mathseduc.models.Lobby
|
||||
import com.example.mathseduc.models.Utiliser
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class ControllerLobby {
|
||||
companion object {
|
||||
fun getLobbies(): ArrayList<Lobby>? {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/all/lobbies/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Lobby>>() {}.type
|
||||
|
||||
return gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getIdQuestionsLobby(idLobby: Int): ArrayList<String> {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/propose/$idLobby/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Map<String, String>>>() {}.type
|
||||
|
||||
val questionList: ArrayList<Map<String, String>> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
|
||||
// Extracting question IDs
|
||||
val idList = ArrayList<String>()
|
||||
for (question in questionList) {
|
||||
val id = question["idquestion"]
|
||||
if (id != null) {
|
||||
idList.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
return idList
|
||||
}
|
||||
}
|
||||
|
||||
fun createLobby(lobbyData: MultipartBody.Builder): Int {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/add/lobbies/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.post(lobbyData.build())
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (response.isSuccessful) {
|
||||
// Si la réponse est réussie, extraire l'ID du lobby du corps de la réponse JSON
|
||||
val responseBody = response.body?.string()
|
||||
val jsonObject = JSONObject(responseBody)
|
||||
|
||||
// Retourner l'ID du lobby
|
||||
return jsonObject.optInt("lobby_id", -1)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("CreateLobby", "Error creating lobby", e)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
fun playerCreatorIdPresentInLobby(idPlayer: Int, lobbyId: Int?): Boolean {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/lobbies/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Lobby>>() {}.type
|
||||
|
||||
val lobby: ArrayList<Lobby> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
return lobby[0].idplayercreator == idPlayer
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun deleteLobby(lobbyId: Int) {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access - Suppression du lobby
|
||||
val deleteRequest = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/lobbies/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.delete()
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
val deleteResponse: Response = client.newCall(deleteRequest).execute()
|
||||
|
||||
// Vérifier si la suppression a réussi
|
||||
if (!deleteResponse.isSuccessful) {
|
||||
Log.e("deleteLobby", "Error deleting lobby")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("deleteLobby", "Error deleting lobby", e)
|
||||
}
|
||||
}
|
||||
fun updateLobbyIdCreatorLobby(lobbyId: Int,lobbyData: MultipartBody.Builder) {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access - Mise à jour du lobby
|
||||
val updateRequest = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/update/lobbies/idplayercreator/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
// Ajoutez d'autres paramètres ou données pour la mise à jour si nécessaire
|
||||
.post(lobbyData.build())
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
val updateResponse: Response = client.newCall(updateRequest).execute()
|
||||
|
||||
// Vérifier si la mise à jour a réussi
|
||||
if (!updateResponse.isSuccessful) {
|
||||
Log.e("updateLobby", "Error updating lobby")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("updateLobby", "Error updating lobby", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun getLobbyUtiliserPlayerTime(lobbyId: Int, playerId: Int): Int {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access - Mise à jour du lobby
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/lobbies/utiliser/playertime/$lobbyId/$playerId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Utiliser>>() {}.type
|
||||
|
||||
val utiliser: ArrayList<Utiliser> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
return utiliser[0].playertime
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("updateLobby", "Error updating lobby", e)
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
fun updateLobbyUtiliserPlayerTime(lobbyId: Int,playerId: Int,lobbyData: MultipartBody.Builder) {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access - Mise à jour du lobby
|
||||
val updateRequest = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/update/lobbies/utiliser/playertime/$lobbyId/$playerId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO\n")
|
||||
.post(lobbyData.build())
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
val updateResponse: Response = client.newCall(updateRequest).execute()
|
||||
|
||||
// Vérifier si la mise à jour a réussi
|
||||
if (!updateResponse.isSuccessful) {
|
||||
Log.e("updateLobby", "Error updating lobby")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("updateLobby", "Error updating lobby", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateLobbyLauched(lobbyId: Int?,lobbyData: MultipartBody.Builder) {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access - Mise à jour du lobby
|
||||
val updateRequest = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/update/lobbies/launched/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
// Ajoutez d'autres paramètres ou données pour la mise à jour si nécessaire
|
||||
.post(lobbyData.build())
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
val updateResponse: Response = client.newCall(updateRequest).execute()
|
||||
|
||||
// Vérifier si la mise à jour a réussi
|
||||
if (!updateResponse.isSuccessful) {
|
||||
Log.e("updateLobby", "Error updating lobby")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("updateLobby", "Error updating lobby", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun getNbPlayerInLobby(lobbyId: Int?): Int {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/utiliser/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Utiliser>>() {}.type
|
||||
|
||||
val utiliser: ArrayList<Utiliser> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
return utiliser.size
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
fun getPlayerInLobby(lobbyId: Int): List<Utiliser> {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/utiliser/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<List<Utiliser>>() {}.type
|
||||
|
||||
return gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
}
|
||||
}
|
||||
|
||||
fun lobbyIsLaunched(lobbyId: Int?): Boolean {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/lobbies/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Lobby>>() {}.type
|
||||
|
||||
val lobby: ArrayList<Lobby> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
return lobby[0].launched == 1
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
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.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.mindrot.jbcrypt.BCrypt
|
||||
import java.io.IOException
|
||||
|
||||
class ControllerPlayer {
|
||||
companion object {
|
||||
|
||||
fun getPlayersIdFromLobbyId(lobbyId: String?): List<Int>? {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
val client = OkHttpClient()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/utiliser/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<List<Utiliser>>() {}.type
|
||||
|
||||
val playerInfoList: List<Utiliser> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
|
||||
// Extract player IDs from PlayerInfo list
|
||||
val playerIds: List<Int> = playerInfoList.map { it.idplayer }
|
||||
|
||||
return playerIds
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun getPlayerInfoById(playerId: String): Player? {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
val client = OkHttpClient()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/players/$playerId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
val gson = Gson()
|
||||
val playerInfo: List<Player> = gson.fromJson(response.body!!.string(), object : TypeToken<List<Player>>() {}.type)
|
||||
|
||||
// Assuming the API returns a list even for a single player, we take the first item
|
||||
return playerInfo.firstOrNull()
|
||||
}
|
||||
|
||||
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 deletePlayer(playerId: String) {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access - Suppression du player
|
||||
val deleteRequest = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/delete/player/$playerId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.delete()
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
val deleteResponse: Response = client.newCall(deleteRequest).execute()
|
||||
|
||||
// Vérifier si la suppression a réussi
|
||||
if (!deleteResponse.isSuccessful) {
|
||||
Log.e("deletePlayer", "Error deleting player")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("deletePlayer", "Error deleting player", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePlayer(playerId: String,lobbyData: MultipartBody.Builder) {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
val client = OkHttpClient()
|
||||
|
||||
val updateRequest = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/update/player/${playerId}/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.post(lobbyData.build())
|
||||
.build()
|
||||
|
||||
val updateResponse: Response = client.newCall(updateRequest).execute()
|
||||
|
||||
if (!updateResponse.isSuccessful) {
|
||||
Log.e("updatePlayer", "Error updating player")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("updatePlayer", "Error updating lobby", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun authenticateUser(nickname: String, password: String): Int {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/verif/player/$nickname/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (response.isSuccessful) {
|
||||
val responseData = response.body?.string()
|
||||
|
||||
// Use Gson to parse the JSON response
|
||||
val gson = Gson()
|
||||
val playerListType = object : TypeToken<List<Player>>() {}.type
|
||||
val playerList: List<Player> = gson.fromJson(responseData, playerListType)
|
||||
|
||||
// Check if the list is not empty
|
||||
if (playerList.isNotEmpty()) {
|
||||
val storedPasswordHash = playerList[0].password
|
||||
|
||||
// Use BCrypt to check if the provided password matches the stored hash
|
||||
if(BCrypt.checkpw(password, storedPasswordHash)){
|
||||
return playerList[0].id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package com.example.mathseduc.controllers
|
||||
|
||||
import Answer
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.StrictMode
|
||||
import com.example.mathseduc.models.Question
|
||||
import com.google.gson.Gson
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.json.JSONArray
|
||||
|
||||
|
||||
class ControllerQuestion {
|
||||
companion object {
|
||||
fun getQuestionsForLobby(questionIds: ArrayList<String>): List<Question>? {
|
||||
val questions = ArrayList<Question>()
|
||||
|
||||
for (questionId in questionIds) {
|
||||
val question = getQuestionById(questionId)
|
||||
if (question != null) {
|
||||
questions.add(question)
|
||||
}
|
||||
}
|
||||
|
||||
return if (questions.isNotEmpty()) questions else null
|
||||
}
|
||||
|
||||
private fun getQuestionById(questionId: String): Question? {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/questions/$questionId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
// Handle error, log, or throw an exception as needed
|
||||
return null
|
||||
}
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val responseData = response.body?.string() ?: return null
|
||||
val jsonArray = JSONArray(responseData)
|
||||
|
||||
val answers = mutableListOf<Answer>()
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val jsonObject = jsonArray.getJSONObject(i)
|
||||
val answer = Answer(
|
||||
id = jsonObject.getInt("a_id"),
|
||||
content = jsonObject.getString("a_content")
|
||||
)
|
||||
answers.add(answer)
|
||||
}
|
||||
|
||||
val questions = mutableListOf<Question>()
|
||||
val jsonObject = jsonArray.getJSONObject(0)
|
||||
val question = Question(
|
||||
id = jsonObject.getInt("q_id"),
|
||||
content = jsonObject.getString("q_content"),
|
||||
nbfails = jsonObject.getInt("nbfails"),
|
||||
difficulty = jsonObject.getInt("difficulty"),
|
||||
idchapter = jsonObject.getInt("idchapter"),
|
||||
idanswergood = jsonObject.getInt("idanswergood"),
|
||||
answers = answers
|
||||
)
|
||||
questions.add(question)
|
||||
|
||||
return if (questions.isNotEmpty()) questions[0] else null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package com.example.mathseduc.controllers
|
||||
|
||||
import android.os.StrictMode
|
||||
import android.util.Log
|
||||
import com.example.mathseduc.models.Lobby
|
||||
import com.example.mathseduc.models.Utiliser
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
|
||||
class ControllerUtiliser {
|
||||
companion object {
|
||||
|
||||
fun createUtiliserByIdLobby(utiliserData: MultipartBody.Builder): Boolean {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/add/utiliser/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.post(utiliserData.build())
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
// Vérifier si la création du lobby a réussi
|
||||
return response.isSuccessful
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("CreateLobby", "Error creating lobby", e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun DeleteUtiliserForLobby(idPlayerConnected: Int, lobbyId: Int): Boolean {
|
||||
try {
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/delete/utiliser/$lobbyId/$idPlayerConnected/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.delete()
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
val response: Response = client.newCall(request).execute()
|
||||
|
||||
// Vérifier si la suppression a réussi
|
||||
return response.isSuccessful
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("DeleteUtiliser", "Error deleting utiliser", e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
fun getIdNextPlayerInLobby(lobbyId: Int): Int {
|
||||
|
||||
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
|
||||
// Client HTTP API
|
||||
val client = OkHttpClient()
|
||||
|
||||
// API Access
|
||||
val request = Request.Builder()
|
||||
.url("https://trusting-panini.87-106-126-109.plesk.page/api/utiliser/$lobbyId/qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO")
|
||||
.build()
|
||||
|
||||
// API Response
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
// Gson deserialization
|
||||
val gson = Gson()
|
||||
val typeTokenProduct = object : TypeToken<ArrayList<Utiliser>>() {}.type
|
||||
|
||||
val utiliser: ArrayList<Utiliser> = gson.fromJson(response.body!!.string(), typeTokenProduct)
|
||||
if (utiliser.isNotEmpty()){
|
||||
return utiliser[0].idplayer
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
data class Answer(
|
||||
val id: Int,
|
||||
val content: String
|
||||
)
|
@ -0,0 +1,6 @@
|
||||
package com.example.mathseduc.models
|
||||
|
||||
data class Chapter(
|
||||
val id: String,
|
||||
val name: String,
|
||||
)
|
@ -0,0 +1,51 @@
|
||||
package com.example.mathseduc.models
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class Lobby(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val password: String,
|
||||
val nbplayers: Int,
|
||||
val idplayercreator: Int,
|
||||
val idchapter: Int,
|
||||
val difficulty: Int,
|
||||
val launched: Int
|
||||
) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readInt(),
|
||||
parcel.readString() ?: "",
|
||||
parcel.readString() ?: "",
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt()
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(id)
|
||||
parcel.writeString(name)
|
||||
parcel.writeString(password)
|
||||
parcel.writeInt(nbplayers)
|
||||
parcel.writeInt(idplayercreator)
|
||||
parcel.writeInt(idchapter)
|
||||
parcel.writeInt(difficulty)
|
||||
parcel.writeInt(launched)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Lobby> {
|
||||
override fun createFromParcel(parcel: Parcel): Lobby {
|
||||
return Lobby(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Lobby?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.example.mathseduc.models
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class Player(
|
||||
val id: Int,
|
||||
val nickname: String,
|
||||
val password: String
|
||||
) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readInt(),
|
||||
parcel.readString() ?: "",
|
||||
parcel.readString() ?: ""
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(id)
|
||||
parcel.writeString(nickname)
|
||||
parcel.writeString(password)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<Player> {
|
||||
override fun createFromParcel(parcel: Parcel): Player {
|
||||
return Player(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Player?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.example.mathseduc.models
|
||||
|
||||
import Answer
|
||||
|
||||
data class Question(
|
||||
val id: Int,
|
||||
val content: String,
|
||||
val nbfails: Int,
|
||||
val difficulty: Int,
|
||||
val idchapter: Int,
|
||||
val idanswergood: Int,
|
||||
val answers: List<Answer>
|
||||
)
|
@ -0,0 +1,6 @@
|
||||
package com.example.mathseduc.models
|
||||
data class Utiliser (
|
||||
val idlobby: Int,
|
||||
val idplayer: Int,
|
||||
val playertime: Int
|
||||
)
|
@ -0,0 +1,315 @@
|
||||
package com.example.mathseduc.ui
|
||||
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.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.ArrowDropDown
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
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.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
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.unit.IntSize
|
||||
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.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.navigation.NavController
|
||||
import com.example.mathseduc.MainActivity
|
||||
import com.example.mathseduc.controllers.ControllerChapter
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerUtiliser
|
||||
import com.example.mathseduc.ui.theme.Colors
|
||||
import com.example.mathseduc.viewModel.CreateLobbyViewModel
|
||||
import com.example.mathseduc.viewModel.ServerDetailsViewModel
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
@Composable
|
||||
fun CreateLobbyPage(navController: NavController) {
|
||||
val viewModelStoreOwner = LocalContext.current as ViewModelStoreOwner
|
||||
|
||||
val viewModel = ViewModelProvider(viewModelStoreOwner).get(CreateLobbyViewModel::class.java)
|
||||
|
||||
val difficultyOptions = listOf("Facile", "Moyen", "Difficile")
|
||||
|
||||
val lobbyName by viewModel.lobbyName.collectAsState("")
|
||||
val password by viewModel.password.collectAsState("")
|
||||
val nbPlayers by viewModel.nbPlayers.collectAsState("")
|
||||
val difficulty by viewModel.difficulty.collectAsState("")
|
||||
val chapter by viewModel.chapter.collectAsState("")
|
||||
val expandedDifficulty by viewModel.expandedDifficulty.collectAsState(false)
|
||||
val expandedChapter by viewModel.expandedChapter.collectAsState(false)
|
||||
val size by viewModel.size.collectAsState(IntSize.Zero)
|
||||
|
||||
val context = LocalContext.current
|
||||
val activity = LocalView.current.context as Activity
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val isPortrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
|
||||
val windowInsetsController = remember {
|
||||
WindowCompat.getInsetsController(activity.window, activity.window.decorView)
|
||||
}
|
||||
|
||||
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(if (isPortrait) 16.dp else 5.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = "Create Lobby",
|
||||
fontSize = (if(isPortrait) 30.sp else 0.sp),
|
||||
color = Color.White,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(if(isPortrait)25.dp else 0.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.background(Colors.White, shape = RoundedCornerShape(8.dp)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
TextField(
|
||||
value = lobbyName,
|
||||
onValueChange = { viewModel.updateLobbyName(it) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.padding(if (isPortrait) 8.dp else 4.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
label = { Text("Lobby Name") }
|
||||
)
|
||||
|
||||
|
||||
TextField(
|
||||
value = password,
|
||||
onValueChange = { viewModel.updatePassword(it) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.padding(if (isPortrait) 8.dp else 4.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
label = { Text("Password") },
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Password)
|
||||
)
|
||||
|
||||
|
||||
TextField(
|
||||
value = nbPlayers,
|
||||
onValueChange = { viewModel.updateNbPlayers(it) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.padding(if (isPortrait) 8.dp else 4.dp),
|
||||
label = { Text("Number of Players") },
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
|
||||
)
|
||||
|
||||
|
||||
Box{
|
||||
TextField(
|
||||
value = difficulty,
|
||||
onValueChange = { viewModel.updateDifficulty(it) },
|
||||
readOnly = true,
|
||||
enabled = false,
|
||||
colors = TextFieldDefaults.colors(
|
||||
disabledTextColor = Colors.Black,
|
||||
disabledLabelColor = Colors.Black,
|
||||
disabledTrailingIconColor = Colors.Black,
|
||||
disabledIndicatorColor = Color.Black
|
||||
),
|
||||
modifier = Modifier
|
||||
.clickable(onClick = {
|
||||
viewModel.updateExpandedDifficulty(!expandedDifficulty)
|
||||
})
|
||||
.onGloballyPositioned {
|
||||
viewModel.updateSize(it.size)
|
||||
}
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.padding(if (isPortrait) 8.dp else 4.dp),
|
||||
label = { Text("Difficulté") },
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
Icons.Default.ArrowDropDown,
|
||||
contentDescription = null,
|
||||
Modifier.clickable {
|
||||
viewModel.updateExpandedDifficulty(!expandedDifficulty)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenu(
|
||||
expanded = expandedDifficulty,
|
||||
onDismissRequest = { viewModel.updateExpandedDifficulty(false) },
|
||||
modifier = Modifier
|
||||
.width(with(LocalDensity.current) { size.width.toDp() })
|
||||
.padding(if (isPortrait) 8.dp else 4.dp)
|
||||
) {
|
||||
difficultyOptions.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(option, color = Colors.Black) },
|
||||
onClick = {
|
||||
viewModel.updateDifficulty(option)
|
||||
viewModel.updateExpandedDifficulty(false)
|
||||
keyboardController?.hide()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Box{
|
||||
val chapterOptions = ControllerChapter.getChapters()
|
||||
TextField(
|
||||
value = chapter,
|
||||
onValueChange = { viewModel.updatechapter(it) },
|
||||
readOnly = true,
|
||||
enabled = false,
|
||||
colors = TextFieldDefaults.colors(
|
||||
disabledTextColor = Colors.Black,
|
||||
disabledLabelColor = Colors.Black,
|
||||
disabledTrailingIconColor = Colors.Black,
|
||||
disabledIndicatorColor = Color.Black
|
||||
),
|
||||
modifier = Modifier
|
||||
.clickable(onClick = {
|
||||
viewModel.updateExpandedChapter(!expandedChapter)
|
||||
})
|
||||
.onGloballyPositioned {
|
||||
viewModel.updateSize(it.size)
|
||||
}
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.padding(if (isPortrait) 8.dp else 4.dp),
|
||||
label = { Text("Chapitre") },
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
Icons.Default.ArrowDropDown,
|
||||
contentDescription = null,
|
||||
Modifier.clickable {
|
||||
viewModel.updateExpandedChapter(!expandedChapter)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenu(
|
||||
expanded = expandedChapter,
|
||||
onDismissRequest = { viewModel.updateExpandedChapter(false) },
|
||||
modifier = Modifier
|
||||
.width(with(LocalDensity.current) { size.width.toDp() })
|
||||
.padding(if (isPortrait) 8.dp else 4.dp)
|
||||
) {
|
||||
|
||||
chapterOptions?.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(option.name, color = Colors.Black) },
|
||||
onClick = {
|
||||
viewModel.updatechapter( option.name )
|
||||
viewModel.updateExpandedChapter(false)
|
||||
keyboardController?.hide()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
// Handle button click (Create Lobby)
|
||||
val selectedChapter = ControllerChapter.getChapters()?.find { it.name == chapter }
|
||||
val difficultyNum = difficultyOptions.indexOf(difficulty) + 1
|
||||
|
||||
if (lobbyName.isBlank() || nbPlayers.isBlank() || nbPlayers.toInt() <= 0 || difficulty.isBlank() || selectedChapter == null) {
|
||||
// Show error message if any field is invalid
|
||||
Toast.makeText(context, "Invalid input. Please check all fields.", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
try {
|
||||
val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
|
||||
// Add fields to form data
|
||||
formDataBuilder.addFormDataPart("name", lobbyName)
|
||||
formDataBuilder.addFormDataPart("password", password)
|
||||
formDataBuilder.addFormDataPart("nbplayers", nbPlayers)
|
||||
formDataBuilder.addFormDataPart("idplayercreator", MainActivity.idPlayerConnected.toString())
|
||||
formDataBuilder.addFormDataPart("idchapter", selectedChapter.id)
|
||||
formDataBuilder.addFormDataPart("difficulty", difficultyNum.toString())
|
||||
Log.e("formData : ",formDataBuilder.toString())
|
||||
// Call createLobby function from ControllerLobby
|
||||
val lobbyId = ControllerLobby.createLobby(formDataBuilder)
|
||||
|
||||
// Check if lobby creation is successful
|
||||
if (lobbyId != -1) {
|
||||
val formDataBuilderConnexion = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
formDataBuilderConnexion.addFormDataPart("idplayer", MainActivity.idPlayerConnected.toString())
|
||||
formDataBuilderConnexion.addFormDataPart("idlobby", lobbyId.toString())
|
||||
formDataBuilderConnexion.addFormDataPart("playertime", "0")
|
||||
|
||||
ControllerUtiliser.createUtiliserByIdLobby(formDataBuilderConnexion)
|
||||
|
||||
Toast.makeText(context, "Lobby created successfully!", Toast.LENGTH_SHORT).show()
|
||||
navController.navigate("serverDetails/${lobbyName}/${lobbyId}/${selectedChapter.id.toInt()}/${nbPlayers}/${difficultyNum}")
|
||||
} else {
|
||||
Toast.makeText(context, "Failed to create lobby. Please try again.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log en cas d'erreur
|
||||
Log.e("CreateLobby", "Error creating lobby", e)
|
||||
Toast.makeText(context, "Failed to create lobby. Please try again.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.padding(if (isPortrait) 8.dp else 4.dp)
|
||||
) {
|
||||
Text(text = "Create Lobby", color = Colors.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,160 @@
|
||||
package com.example.mathseduc.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.navigation.NavController
|
||||
import com.example.mathseduc.ConnexionPlayerActivity
|
||||
import com.example.mathseduc.MainActivity
|
||||
import com.example.mathseduc.MultiActivity
|
||||
import com.example.mathseduc.QuizMultiActivity
|
||||
import com.example.mathseduc.R
|
||||
import com.example.mathseduc.ui.theme.Colors
|
||||
|
||||
|
||||
@Composable
|
||||
fun HomePage(navController: NavController) {
|
||||
val context = LocalContext.current
|
||||
val modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
|
||||
val view = LocalView.current
|
||||
val isPortrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
val window = (view.context as Activity).window
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
val windowInsetsController = remember {
|
||||
WindowCompat.getInsetsController(window, window.decorView)
|
||||
}
|
||||
|
||||
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.logo),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(160.dp, 130.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Button(
|
||||
onClick = {navController.navigate("themeChoice")},
|
||||
shape = RoundedCornerShape(15),
|
||||
colors = ButtonDefaults.buttonColors(Colors.Green),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.height(48.dp)
|
||||
|
||||
) {
|
||||
Text(
|
||||
text = "Solo",
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
if (MainActivity.idPlayerConnected != -1){
|
||||
navController.navigate("multiplayer")
|
||||
}
|
||||
else {
|
||||
Toast.makeText(context, "Vous n'êtes pas connecté", Toast.LENGTH_SHORT).show()
|
||||
navController.navigate("connexion")
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(15),
|
||||
colors = ButtonDefaults.buttonColors(Colors.Orange),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.height(48.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Multiplayer",
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,344 @@
|
||||
package com.example.mathseduc.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
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.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.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
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
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.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.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.NavController
|
||||
import com.example.mathseduc.MainActivity
|
||||
import com.example.mathseduc.controllers.ControllerChapter
|
||||
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 com.example.mathseduc.viewModel.MultiPageViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MultiPage(navController: NavController) {
|
||||
val context = LocalContext.current
|
||||
val activity = LocalView.current.context as Activity
|
||||
|
||||
val viewModel = ViewModelProvider(navController.getViewModelStoreOwner(navController.graph.id)).get(MultiPageViewModel::class.java)
|
||||
val lobbyList by viewModel.lobbyList.collectAsState(initial = emptyList())
|
||||
|
||||
var selectedItem : Lobby? = null
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
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())
|
||||
|
||||
|
||||
// Fonction pour actualiser la liste des lobbies
|
||||
suspend fun refreshLobbyList() {
|
||||
viewModel.updateLobbyList()
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
val refreshRunnable = {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
refreshLobbyList()
|
||||
} catch (e: Exception) {
|
||||
Log.e("MainActivity", "Error refreshing lobbies: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshRunnable.invoke()
|
||||
|
||||
onDispose {
|
||||
}
|
||||
}
|
||||
|
||||
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(start = 16.dp, top = 62.dp, end = 16.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "Liste des lobbies",
|
||||
color = Colors.White,
|
||||
fontSize = 17.sp,
|
||||
modifier = Modifier
|
||||
.weight(if (isPortrait) 1f else 0.6f)
|
||||
.padding(start = 25.dp)
|
||||
)
|
||||
Button(
|
||||
onClick = {
|
||||
navController.navigate("createLobby")
|
||||
},
|
||||
shape = RoundedCornerShape(15),
|
||||
modifier = Modifier
|
||||
.weight(if (isPortrait) 1f else 0.4f)
|
||||
.padding(end = 20.dp),
|
||||
colors = ButtonDefaults.buttonColors(Colors.Blue)
|
||||
) {
|
||||
Text(text = "Ajouter un lobby")
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.border(2.dp, Color.White, shape = RoundedCornerShape(3))
|
||||
.weight(0.85f),
|
||||
) {
|
||||
items(lobbyList) { lobby ->
|
||||
LobbyItem(lobby, selectedItem == lobby) {
|
||||
selectedItem = it
|
||||
|
||||
if (viewModel.getNbPlayerInLobby(selectedItem!!) < selectedItem!!.nbplayers) {
|
||||
// Créer un Utiliser si le lobby n'est pas plein
|
||||
|
||||
scope.launch {
|
||||
createUtiliserForLobby(context, selectedItem!!, navController)
|
||||
}
|
||||
} else {
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
scope.launch {
|
||||
refreshLobbyList()
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(15),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
colors = ButtonDefaults.buttonColors(Colors.Blue)
|
||||
) {
|
||||
Text(text = "Actualiser")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private suspend fun createUtiliserForLobby(context: Context, lobby: Lobby, navController: NavController) {
|
||||
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 mainScope = MainScope()
|
||||
mainScope.launch {
|
||||
navController.navigate("serverDetails/${lobby.name}/${lobby.id}/${lobby.idchapter}/${lobby.nbplayers}/${lobby.difficulty}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LobbyItem(lobby: Lobby, isSelected: Boolean, onItemClick: (Lobby) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
var showDialog by rememberSaveable { mutableStateOf(false) }
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
.clickable {
|
||||
if (lobby.password.isNotEmpty()) {
|
||||
showDialog = true
|
||||
} else {
|
||||
onItemClick(lobby)
|
||||
}
|
||||
}
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if (isSelected) Colors.Orange else Colors.Grey)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = lobby.name,
|
||||
fontSize = 18.sp,
|
||||
color = Colors.White,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = "${ControllerLobby.getNbPlayerInLobby(lobby.id)}/${lobby.nbplayers}",
|
||||
fontSize = 18.sp,
|
||||
color = (if(ControllerLobby.getNbPlayerInLobby(lobby.id)==lobby.nbplayers) Color.Red else Color.White),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = ControllerChapter.getChapterNameById(lobby.idchapter).toString(),
|
||||
fontSize = 18.sp,
|
||||
color = Colors.White,
|
||||
modifier = Modifier.weight(3f)
|
||||
)
|
||||
Text(
|
||||
text = lobby.difficulty.toString(),
|
||||
fontSize = 18.sp,
|
||||
color = Colors.White,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
if (showDialog) {
|
||||
ConfirmDialog(
|
||||
onDismiss = { showDialog = false },
|
||||
onConfirm = { password ->
|
||||
println("Password input : $password")
|
||||
if (password == lobby.password) {
|
||||
showDialog = false
|
||||
onItemClick(lobby)
|
||||
} else {
|
||||
Toast.makeText(context, "Wrong password!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmDialog(onDismiss: () -> Unit, onConfirm: (String) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
var password by remember { mutableStateOf("") }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
onDismiss()
|
||||
},
|
||||
title = { Text("Enter Password") },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onConfirm(password)
|
||||
}
|
||||
) {
|
||||
Text("Confirm")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
}
|
||||
) {
|
||||
Text("Dismiss")
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,320 @@
|
||||
package com.example.mathseduc.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.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.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.controllers.ControllerPlayer
|
||||
import com.example.mathseduc.ui.theme.Colors
|
||||
import okhttp3.MultipartBody
|
||||
import org.mindrot.jbcrypt.BCrypt
|
||||
|
||||
@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) }
|
||||
var confirmDialog 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 = { MainActivity.idPlayerConnected = -1
|
||||
navController.navigate("home") },
|
||||
shape = RoundedCornerShape(15),
|
||||
colors = ButtonDefaults.buttonColors(Colors.Blue),
|
||||
modifier = Modifier
|
||||
.height(48.dp)
|
||||
.padding(horizontal = 6.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "disconnection",
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Button(
|
||||
onClick = { confirmDialog = true },
|
||||
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(),player.nickname,onDismiss = { showDialog = false })
|
||||
}
|
||||
}
|
||||
if (confirmDialog) {
|
||||
if (player != null) {
|
||||
DeleteConfirmDialog(onConfirmDelete = { ControllerPlayer.deletePlayer(player.id.toString())
|
||||
MainActivity.idPlayerConnected = -1
|
||||
navController.navigate("home")
|
||||
confirmDialog = false},onDismiss = { confirmDialog = false })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChangeDialog(playerId: String, currentNickname: String, onDismiss: () -> Unit) {
|
||||
|
||||
var nickname by rememberSaveable { mutableStateOf(currentNickname) }
|
||||
var password by rememberSaveable { mutableStateOf("") }
|
||||
|
||||
var nicknameError by remember { mutableStateOf(false) }
|
||||
var passwordError by remember { mutableStateOf(false) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
onDismiss()
|
||||
},
|
||||
title = { Text("Change Information") },
|
||||
confirmButton = {
|
||||
Button(
|
||||
colors = ButtonDefaults.buttonColors(Colors.Cyan),
|
||||
onClick = {
|
||||
if (nickname.isNotBlank() && password.isNotBlank()) {
|
||||
val hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt())
|
||||
val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
formDataBuilder.addFormDataPart("nickname", nickname)
|
||||
formDataBuilder.addFormDataPart("password", hashedPassword)
|
||||
ControllerPlayer.updatePlayer(playerId, formDataBuilder)
|
||||
onDismiss()
|
||||
} else {
|
||||
if (nickname.isBlank()) nicknameError = true
|
||||
if (password.isBlank()) passwordError = true
|
||||
}
|
||||
}
|
||||
) {
|
||||
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
|
||||
nicknameError = false
|
||||
},
|
||||
label = { Text("Nickname") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
isError = nicknameError
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = {
|
||||
password = it
|
||||
passwordError = false
|
||||
},
|
||||
label = { Text("Password") },
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Password
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
isError = passwordError
|
||||
)
|
||||
|
||||
if (nicknameError || passwordError) {
|
||||
Text(
|
||||
text = "Please fill in all fields",
|
||||
color = Color.Red,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun DeleteConfirmDialog(onConfirmDelete: () -> Unit, onDismiss: () -> Unit) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text("Confirm deleting account") },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onConfirmDelete()
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(Colors.Red)
|
||||
) {
|
||||
Text("Delete",color = Color.White)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onDismiss()
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(Color.LightGray)
|
||||
) {
|
||||
Text("Cancel",color = Color.White)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text("Are you sure you want to delete your account?")
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,223 @@
|
||||
package com.example.mathseduc.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.lifecycle.ViewModelProvider
|
||||
import androidx.navigation.NavController
|
||||
import com.example.mathseduc.MainActivity
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerQuestion
|
||||
import com.example.mathseduc.ui.theme.Colors
|
||||
import com.example.mathseduc.viewModel.QuizMultiViewModel
|
||||
import kotlinx.coroutines.channels.ticker
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
@Composable
|
||||
fun QuizMultiScreen(navController: NavController, lobbyId: Int?) {
|
||||
val context = LocalContext.current
|
||||
val activity = LocalView.current.context as Activity
|
||||
|
||||
val viewModel = ViewModelProvider(navController.getViewModelStoreOwner(navController.graph.id)).get(QuizMultiViewModel::class.java)
|
||||
|
||||
val chronoValue by viewModel.chronoValue.collectAsState(0.0f)
|
||||
val listQuestion by viewModel.listQuestion.collectAsState(ControllerQuestion.getQuestionsForLobby(ControllerLobby.getIdQuestionsLobby(lobbyId!!)))
|
||||
val listPlayer by viewModel.listPlayer.collectAsState(ControllerLobby.getPlayerInLobby(lobbyId!!))
|
||||
val currentQuestionIndex by viewModel.currentQuestionIndex.collectAsState(0)
|
||||
val progressBarValues by viewModel.progressBarValues.collectAsState(List(listPlayer.size) { 0.0f })
|
||||
val progressBarTotalValues by viewModel.progressBarTotalValues.collectAsState(List(listPlayer.size) { 0.0f })
|
||||
val quizFinished by viewModel.quizFinished.collectAsState(false)
|
||||
val windowInsetsController by viewModel.windowInsetsController.collectAsState(WindowCompat.getInsetsController(activity.window, activity.window.decorView))
|
||||
|
||||
windowInsetsController?.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
|
||||
windowInsetsController?.hide(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
viewModel.updateListQuestion(lobbyId)
|
||||
viewModel.updateListPlayer(lobbyId)
|
||||
viewModel.initializeProgressBar()
|
||||
viewModel.initializeProgressBarTotal()
|
||||
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
val timer = ticker(delayMillis = 1000) // Update every 1000 milliseconds
|
||||
for (tick in timer) {
|
||||
if (quizFinished) break
|
||||
for ((index, player) in listPlayer.withIndex()) {
|
||||
viewModel.updateProgressBarValues(index)
|
||||
}
|
||||
viewModel.updateChronoValue(1f)
|
||||
if (chronoValue >= 30f) {
|
||||
viewModel.updateCurrentQuestionIndex()
|
||||
viewModel.resetChronoValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
val timer = ticker(delayMillis = 3000)
|
||||
for (tick in timer) {
|
||||
if (quizFinished) break
|
||||
var valueBD = ControllerLobby.getPlayerInLobby(lobbyId!!)
|
||||
for ((index, player) in listPlayer.withIndex()) {
|
||||
viewModel.updateProgressBarTotalValues(index,valueBD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
|
||||
for ((index, player) in listPlayer.withIndex()) {
|
||||
CustomProgressBar(progressBarTotalValues.toMutableList()[index])
|
||||
}
|
||||
|
||||
// Chrono ProgressBar
|
||||
ChronoProgressBar(chronoValue)
|
||||
|
||||
if (currentQuestionIndex < listQuestion!!.size) {
|
||||
val currentQuestion = listQuestion!![currentQuestionIndex]
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(8.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(2f)
|
||||
.padding(vertical = 20.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(5f)
|
||||
.background(color = Colors.Grey, shape = RoundedCornerShape(8.dp))
|
||||
) {
|
||||
Text(
|
||||
text = currentQuestion.content,
|
||||
modifier = Modifier.padding(16.dp),
|
||||
color = Colors.White,
|
||||
fontSize = 20.sp,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
currentQuestion.answers.forEachIndexed { index, answer ->
|
||||
val buttonBackgroundColor = when (index) {
|
||||
0 -> Colors.Red
|
||||
1 -> Colors.Blue
|
||||
2 -> Colors.Green
|
||||
else -> Colors.Cyan
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
if (answer.id == currentQuestion.idanswergood) {
|
||||
Toast.makeText(context, "Réussi !!", Toast.LENGTH_SHORT).show()
|
||||
|
||||
val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
val playertime = ControllerLobby.getLobbyUtiliserPlayerTime(lobbyId!!,MainActivity.idPlayerConnected) + 10
|
||||
formDataBuilder.addFormDataPart("playertime", playertime.toString())
|
||||
|
||||
ControllerLobby.updateLobbyUtiliserPlayerTime(lobbyId,MainActivity.idPlayerConnected,formDataBuilder)
|
||||
} else {
|
||||
Toast.makeText(context, "Raté !!", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
viewModel.resetChronoValue()
|
||||
viewModel.updateCurrentQuestionIndex()
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 10.dp),
|
||||
shape = RoundedCornerShape(15),
|
||||
colors = ButtonDefaults.buttonColors(buttonBackgroundColor)
|
||||
) {
|
||||
Text(text = answer.content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.padding(vertical = 10.dp)
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.updateCurrentQuestionIndex()
|
||||
},
|
||||
modifier = Modifier
|
||||
.weight(2f),
|
||||
shape = RoundedCornerShape(15),
|
||||
colors = ButtonDefaults.buttonColors(Colors.Grey),
|
||||
) {
|
||||
Text(text = "Passer")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, "Fini !!", Toast.LENGTH_SHORT).show()
|
||||
navController.navigate("home")
|
||||
viewModel.updateQuizFinished(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CustomProgressBar(progressBarValue: Float) {
|
||||
LinearProgressIndicator(
|
||||
progress = progressBarValue / 100f,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChronoProgressBar(chronoValue: Float) {
|
||||
CircularProgressIndicator(
|
||||
progress = chronoValue / 30f,
|
||||
modifier = Modifier.padding(horizontal = 10.dp)
|
||||
)
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
package com.example.mathseduc.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Configuration
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
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.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.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.navigation.NavController
|
||||
import com.example.mathseduc.MainActivity
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerUtiliser
|
||||
import com.example.mathseduc.ui.theme.Colors
|
||||
import com.example.mathseduc.viewModel.ServerDetailsViewModel
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
@Composable
|
||||
fun LeaveLobbyDialog(namelobby: String,lobbyId : Int?, onConfirmLeave: () -> Unit, onCancelLeave: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onCancelLeave,
|
||||
title = { Text("Confirm leaving lobby") },
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
if (lobbyId != null) {
|
||||
myBackPressed(lobbyId)
|
||||
}
|
||||
onConfirmLeave()
|
||||
}
|
||||
) {
|
||||
Text("Leave")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
onCancelLeave()
|
||||
}
|
||||
) {
|
||||
Text("Cancel")
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text("Are you sure you want to leave the lobby $namelobby?")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ServerDetailPage(navController: NavController, serverName: String?, lobbyId: Int?, chapterId: Int?, nbPlayers: Int?, lobbyDifficulty: Int?) {
|
||||
val context = LocalContext.current
|
||||
val viewModelStoreOwner = LocalContext.current as ViewModelStoreOwner
|
||||
val viewModel = ViewModelProvider(viewModelStoreOwner).get(ServerDetailsViewModel::class.java)
|
||||
|
||||
val playerList by viewModel.playerList.collectAsState(initial = emptyList())
|
||||
val playerListInfos by viewModel.playerListInfos.collectAsState(initial = emptyList())
|
||||
val isCreator by viewModel.isCreator.collectAsState(false)
|
||||
val refreshState by viewModel.refreshState.collectAsState(true)
|
||||
val showDialog by viewModel.showDialog.collectAsState(false)
|
||||
|
||||
val isPortrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
val activity = LocalView.current.context as Activity
|
||||
|
||||
val windowInsetsController = remember {
|
||||
WindowCompat.getInsetsController(activity.window, activity.window.decorView)
|
||||
}
|
||||
|
||||
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
DisposableEffect(refreshState) {
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
val refreshRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
viewModel.updatePlayerList(lobbyId)
|
||||
viewModel.updatePlayerListInfos()
|
||||
viewModel.updateIsCreator(lobbyId)
|
||||
|
||||
if(viewModel.Launchedlobby(lobbyId)){
|
||||
/*
|
||||
val intent = Intent(context, QuizMultiActivity::class.java)
|
||||
intent.putExtra("serverName", serverName)
|
||||
intent.putExtra("lobbyId", lobbyId)
|
||||
intent.putExtra("chapterId", chapterId)
|
||||
intent.putExtra("nbPlayers", nbPlayers)
|
||||
intent.putExtra("lobbyDifficulty", lobbyDifficulty)
|
||||
context.startActivity(intent)
|
||||
*/
|
||||
navController.navigate("quizMultiScreen/${lobbyId}")
|
||||
viewModel.updateRefresh(false)
|
||||
}
|
||||
|
||||
if (refreshState){
|
||||
handler.postDelayed(this,3000)
|
||||
Log.e("MainActivity", "Refresh ServerDetails")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handler.post(refreshRunnable)
|
||||
|
||||
onDispose {
|
||||
viewModel.updateRefresh(false)
|
||||
handler.removeCallbacks(refreshRunnable)
|
||||
}
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = Color.Transparent,
|
||||
),
|
||||
title = {},
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = { viewModel.updateShowDialog(true) },
|
||||
modifier = Modifier.size(60.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = "Retour",
|
||||
modifier = Modifier.size(36.dp),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
val modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp, top = 8.dp)
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = serverName!!,
|
||||
color = Color.White,
|
||||
fontSize = 30.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(if(isPortrait)25.dp else 10.dp))
|
||||
|
||||
Text(
|
||||
text = "Lobby Settings",
|
||||
fontSize = 23.sp,
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Text(
|
||||
text = "Chapter : ${viewModel.getChapterNameById(chapterId).toString()}",
|
||||
fontSize = 19.sp,
|
||||
color = Color.White
|
||||
)
|
||||
Row {
|
||||
Text(
|
||||
text = "Players : ${playerListInfos.size}/${nbPlayers}",
|
||||
fontSize = 19.sp,
|
||||
color = (if(viewModel.getNbPlayerInLobby(lobbyId)==nbPlayers) Color.Red else Color.White)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(40.dp))
|
||||
Text(
|
||||
text = "Difficulty : $lobbyDifficulty",
|
||||
fontSize = 19.sp,
|
||||
color = Colors.White,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(if(isPortrait)30.dp else 10.dp))
|
||||
|
||||
Text(
|
||||
text = "Player Name",
|
||||
fontSize = 19.sp,
|
||||
modifier = Modifier.background(Color.Black),
|
||||
color = Colors.White,
|
||||
)
|
||||
Divider(
|
||||
color = Color.Gray,
|
||||
thickness = 2.dp,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.weight(0.75f)
|
||||
.padding(top = 1.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(playerListInfos) { player ->
|
||||
Text(
|
||||
text = player.nickname,
|
||||
fontSize = 18.sp,
|
||||
color = Colors.White,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(top = if (isPortrait) 5.dp else 1.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isCreator) {
|
||||
val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
formDataBuilder.addFormDataPart("launched", "1")
|
||||
Button(
|
||||
onClick = {
|
||||
viewModel.updateLobbyLauched(lobbyId,formDataBuilder)
|
||||
},
|
||||
shape = RoundedCornerShape(15),
|
||||
enabled = isCreator,
|
||||
colors = ButtonDefaults.buttonColors(Colors.Green),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "LAUNCH",
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
if (showDialog) {
|
||||
LeaveLobbyDialog(serverName,lobbyId, onConfirmLeave = {navController.navigate("multiplayer")},onCancelLeave = { viewModel.updateShowDialog(false) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun myBackPressed(lobbyId: Int) {
|
||||
ControllerUtiliser.DeleteUtiliserForLobby(MainActivity.idPlayerConnected, lobbyId)
|
||||
|
||||
if (ControllerLobby.playerCreatorIdPresentInLobby(MainActivity.idPlayerConnected, lobbyId)) {
|
||||
val idNextPlayerCreator = ControllerUtiliser.getIdNextPlayerInLobby(lobbyId)
|
||||
|
||||
if (idNextPlayerCreator == -1) {
|
||||
ControllerLobby.deleteLobby(lobbyId)
|
||||
} else {
|
||||
val formDataBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
|
||||
formDataBuilder.addFormDataPart("idplayercreator", idNextPlayerCreator.toString())
|
||||
ControllerLobby.updateLobbyIdCreatorLobby(lobbyId, formDataBuilder)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.example.mathseduc.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
|
||||
object Colors {
|
||||
val Purple200 = Color(0xFFBB86FC)
|
||||
val Purple500 = Color(0xFF6200EE)
|
||||
val Purple700 = Color(0xFF3700B3)
|
||||
val Teal200 = Color(0xFF03DAC5)
|
||||
val Teal700 = Color(0xFF018786)
|
||||
val Black = Color(0xFF000000)
|
||||
val White = Color(0xFFFFFFFF)
|
||||
val Green = Color(0xFF008000)
|
||||
val Orange = Color(0xFFFFA500)
|
||||
val Grey = Color(0xFF8C92AC)
|
||||
val Blue = Color(0xFF0D6EFD)
|
||||
val Red = Color(0xFFFF0000)
|
||||
val BlackGrey = Color(0xFF5C636A)
|
||||
val Cyan = Color(0xFF00FFFF)
|
||||
val Turquoise = Color(0xFF40E0D0)
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package com.example.mathseduc.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.example.mathseduc.R
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun MathsEducTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.example.mathseduc.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
@ -0,0 +1,214 @@
|
||||
package com.example.mathseduc.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
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.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
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.LocalView
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
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.controllers.ControllerChapter
|
||||
import com.example.mathseduc.ui.theme.Colors
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ThemeChoicePage(navController: NavController){
|
||||
var selectedDifficulty by rememberSaveable { mutableStateOf("") }
|
||||
var selectedChapter by rememberSaveable { mutableStateOf("") }
|
||||
|
||||
val isPortrait = LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT
|
||||
val activity = LocalView.current.context as Activity
|
||||
val chapterOptions = ControllerChapter.getChapters()
|
||||
|
||||
val windowInsetsController = remember {
|
||||
WindowCompat.getInsetsController(activity.window, activity.window.decorView)
|
||||
}
|
||||
|
||||
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
|
||||
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
|
||||
) {
|
||||
Text(
|
||||
text = "Difficulty :",
|
||||
fontSize = 25.sp,
|
||||
color = Color.White,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
|
||||
Row {
|
||||
Button(
|
||||
onClick = { selectedDifficulty = "1"},
|
||||
shape = RoundedCornerShape(20),
|
||||
colors = ButtonDefaults.buttonColors(if (selectedDifficulty == "1") Colors.Green else Color.Transparent),
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = Colors.Green,
|
||||
shape = RoundedCornerShape(20)
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = "Easy",
|
||||
color = Color.White,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(if(isPortrait) 15.dp else 20.dp))
|
||||
Button(
|
||||
onClick = { selectedDifficulty = "2"},
|
||||
shape = RoundedCornerShape(20),
|
||||
colors = ButtonDefaults.buttonColors(if (selectedDifficulty == "2") Colors.Orange else Color.Transparent),
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = Colors.Orange,
|
||||
shape = RoundedCornerShape(20)
|
||||
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = "Medium",
|
||||
color = Color.White,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(if(isPortrait) 15.dp else 20.dp))
|
||||
Button(
|
||||
onClick = { selectedDifficulty = "3"},
|
||||
shape = RoundedCornerShape(20),
|
||||
colors = ButtonDefaults.buttonColors(if (selectedDifficulty == "3") Colors.Red else Color.Transparent),
|
||||
modifier = Modifier
|
||||
.height(60.dp)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = Colors.Red,
|
||||
shape = RoundedCornerShape(20)
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = "Hard",
|
||||
color = Color.White,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(if(isPortrait) 25.dp else 30.dp))
|
||||
|
||||
Text(
|
||||
text = "Chapter :",
|
||||
fontSize = 25.sp,
|
||||
color = Color.White,
|
||||
)
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(15.dp),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp)
|
||||
.horizontalScroll(rememberScrollState())
|
||||
) {
|
||||
if (chapterOptions != null) {
|
||||
chapterOptions.forEach { chapter ->
|
||||
Button(
|
||||
onClick = { selectedChapter = chapter.name },
|
||||
shape = RoundedCornerShape(10),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = Color.Gray,
|
||||
shape = RoundedCornerShape(10)
|
||||
),
|
||||
colors = ButtonDefaults.buttonColors(if (selectedChapter == chapter.name) Color.Gray else Color.Transparent)
|
||||
) {
|
||||
Text(text = chapter.name, modifier = Modifier.padding(horizontal = 1.dp, vertical = 8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(if(isPortrait) 15.dp else 20.dp))
|
||||
|
||||
Button(
|
||||
onClick = {},
|
||||
shape = RoundedCornerShape(15),
|
||||
colors = ButtonDefaults.buttonColors(Colors.Green),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (isPortrait) 1f else 0.5f)
|
||||
.height(48.dp)
|
||||
|
||||
) {
|
||||
Text(
|
||||
text = "Start Game",
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.example.mathseduc.viewModel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.mathseduc.MainActivity
|
||||
import com.example.mathseduc.controllers.ControllerChapter
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerPlayer
|
||||
import com.example.mathseduc.models.Lobby
|
||||
import com.example.mathseduc.models.Player
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class CreateLobbyViewModel : ViewModel() {
|
||||
|
||||
private var lobbyNameState = MutableStateFlow("")
|
||||
val lobbyName : StateFlow<String> = lobbyNameState
|
||||
|
||||
private var passwordState = MutableStateFlow("")
|
||||
val password: StateFlow<String> = passwordState
|
||||
|
||||
private var nbPlayersState = MutableStateFlow("")
|
||||
val nbPlayers: StateFlow<String> = nbPlayersState
|
||||
|
||||
private var difficultyState = MutableStateFlow("")
|
||||
val difficulty: StateFlow<String> = difficultyState
|
||||
|
||||
private var chapterState = MutableStateFlow("")
|
||||
val chapter : StateFlow<String> = chapterState
|
||||
|
||||
private var expandedDifficultyState = MutableStateFlow(false)
|
||||
val expandedDifficulty: StateFlow<Boolean> = expandedDifficultyState
|
||||
|
||||
private var expandedChapterState = MutableStateFlow(false)
|
||||
val expandedChapter: StateFlow<Boolean> = expandedChapterState
|
||||
|
||||
private var sizeState = MutableStateFlow(IntSize.Zero)
|
||||
val size : StateFlow<IntSize> = sizeState
|
||||
|
||||
fun updateLobbyName(lobbyName : String) {
|
||||
this.lobbyNameState.update { lobbyName }
|
||||
}
|
||||
|
||||
fun updatePassword(password : String) {
|
||||
this.passwordState.update { password }
|
||||
}
|
||||
|
||||
fun updateNbPlayers(nbPlayers : String) {
|
||||
this.nbPlayersState.update { nbPlayers }
|
||||
}
|
||||
|
||||
fun updateDifficulty(difficulty : String) {
|
||||
this.difficultyState.update { difficulty }
|
||||
}
|
||||
|
||||
fun updatechapter(chapter : String) {
|
||||
this.chapterState.update { chapter }
|
||||
}
|
||||
|
||||
fun updateExpandedChapter(expandedChapter : Boolean) {
|
||||
this.expandedChapterState.update { expandedChapter }
|
||||
}
|
||||
|
||||
fun updateExpandedDifficulty(expandedDifficulty : Boolean) {
|
||||
this.expandedDifficultyState.update { expandedDifficulty }
|
||||
}
|
||||
|
||||
fun updateSize(size : IntSize) {
|
||||
this.sizeState.update { size }
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.example.mathseduc.viewModel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerPlayer
|
||||
import com.example.mathseduc.models.Lobby
|
||||
import com.example.mathseduc.models.Player
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
class MultiPageViewModel : ViewModel() {
|
||||
|
||||
private var lobbyListState = MutableStateFlow<List<Lobby>>(emptyList())
|
||||
val lobbyList : StateFlow<List<Lobby>> = lobbyListState
|
||||
|
||||
fun updateLobbyList() {
|
||||
this.lobbyListState.update {
|
||||
ControllerLobby.getLobbies() ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun getNbPlayerInLobby(selectedItem : Lobby) : Int {
|
||||
return ControllerLobby.getNbPlayerInLobby(selectedItem!!.id)
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.example.mathseduc.viewModel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerQuestion
|
||||
import com.example.mathseduc.models.Lobby
|
||||
import com.example.mathseduc.models.Player
|
||||
import com.example.mathseduc.models.Question
|
||||
import com.example.mathseduc.models.Utiliser
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.count
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.withIndex
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class QuizMultiViewModel : ViewModel() {
|
||||
|
||||
private var chronoValueState = MutableStateFlow(0.0f)
|
||||
val chronoValue : StateFlow<Float> = chronoValueState
|
||||
|
||||
private var listQuestionState = MutableStateFlow<List<Question>?>(emptyList())
|
||||
val listQuestion : StateFlow<List<Question>?> = listQuestionState
|
||||
|
||||
private var listPlayerState = MutableStateFlow<List<Utiliser>>(emptyList())
|
||||
val listPlayer : StateFlow<List<Utiliser>> = listPlayerState
|
||||
|
||||
private var currentQuestionIndexState = MutableStateFlow(0)
|
||||
val currentQuestionIndex : StateFlow<Int> = currentQuestionIndexState
|
||||
|
||||
private var progressBarValuesState = MutableStateFlow<List<Float>>(emptyList())
|
||||
val progressBarValues : StateFlow<List<Float>> = progressBarValuesState
|
||||
|
||||
private var progressBarTotalValuesState = MutableStateFlow<List<Float>>(emptyList())
|
||||
val progressBarTotalValues : StateFlow<List<Float>> = progressBarTotalValuesState
|
||||
|
||||
private var quizFinishedState = MutableStateFlow(false)
|
||||
val quizFinished : StateFlow<Boolean> = quizFinishedState
|
||||
|
||||
private var windowInsetsControllerState = MutableStateFlow(null)
|
||||
val windowInsetsController : StateFlow<WindowInsetsControllerCompat?> = windowInsetsControllerState
|
||||
|
||||
fun updateListQuestion(lobbyId : Int?){
|
||||
this.listQuestionState.update{ ControllerQuestion.getQuestionsForLobby(ControllerLobby.getIdQuestionsLobby(lobbyId!!)) }
|
||||
}
|
||||
fun updateListPlayer(lobbyId : Int?){
|
||||
this.listPlayerState.update{ ControllerLobby.getPlayerInLobby(lobbyId!!) }
|
||||
}
|
||||
fun initializeProgressBar(){
|
||||
this.progressBarValuesState.update{ List(this.listPlayer.value.size) { 0.0f } }
|
||||
}
|
||||
fun initializeProgressBarTotal(){
|
||||
this.progressBarTotalValuesState.update{ List(this.listPlayer.value.size) { 0.0f } }
|
||||
}
|
||||
fun updateChronoValue(time : Float){
|
||||
this.chronoValueState.update { this.chronoValue.value + time }
|
||||
}
|
||||
fun resetChronoValue(){
|
||||
this.chronoValueState.update { 0.0f }
|
||||
}
|
||||
fun updateCurrentQuestionIndex(){
|
||||
this.currentQuestionIndexState.update { this.currentQuestionIndex.value + 1 }
|
||||
}
|
||||
|
||||
fun updateQuizFinished(bool : Boolean){
|
||||
this.quizFinishedState.update { bool }
|
||||
}
|
||||
|
||||
fun updateProgressBarValues(index : Int){
|
||||
this.progressBarValuesState.update {
|
||||
val newList = it.toMutableList()
|
||||
newList[index] += 1f
|
||||
|
||||
newList
|
||||
}
|
||||
}
|
||||
|
||||
fun updateProgressBarTotalValues(index : Int,valueBD : List<Utiliser>){
|
||||
this.progressBarTotalValuesState.update {
|
||||
val newList = it.toMutableList()
|
||||
newList[index] += this.progressBarValues.value[index] + valueBD[index].playertime.toFloat()
|
||||
|
||||
newList
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package com.example.mathseduc.viewModel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.mathseduc.MainActivity
|
||||
import com.example.mathseduc.controllers.ControllerChapter
|
||||
import com.example.mathseduc.controllers.ControllerLobby
|
||||
import com.example.mathseduc.controllers.ControllerPlayer
|
||||
import com.example.mathseduc.models.Lobby
|
||||
import com.example.mathseduc.models.Player
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import okhttp3.MultipartBody
|
||||
|
||||
class ServerDetailsViewModel : ViewModel() {
|
||||
|
||||
private var playerListState = MutableStateFlow<List<Int>>(emptyList())
|
||||
val playerList : StateFlow<List<Int>> = playerListState
|
||||
|
||||
private var playerListInfosState = MutableStateFlow<List<Player>>(emptyList())
|
||||
val playerListInfos: StateFlow<List<Player>> = playerListInfosState
|
||||
|
||||
private var isCreatorState = MutableStateFlow(false)
|
||||
val isCreator: StateFlow<Boolean> = isCreatorState
|
||||
|
||||
private var showDialogState = MutableStateFlow(false)
|
||||
val showDialog: StateFlow<Boolean> = showDialogState
|
||||
|
||||
private var refreshStateState = MutableStateFlow(true)
|
||||
val refreshState : StateFlow<Boolean> = refreshStateState
|
||||
|
||||
fun updatePlayerList(lobbyId : Int?) {
|
||||
this.playerListState.update {
|
||||
ControllerPlayer.getPlayersIdFromLobbyId(lobbyId.toString()) ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun updatePlayerListInfos() {
|
||||
this.playerListInfosState.update {
|
||||
playerList.value.mapNotNull { playerId -> ControllerPlayer.getPlayerInfoById(playerId.toString()) }
|
||||
}
|
||||
}
|
||||
|
||||
fun updateIsCreator(lobbyId : Int?) {
|
||||
this.isCreatorState.update {
|
||||
ControllerLobby.playerCreatorIdPresentInLobby(MainActivity.idPlayerConnected, lobbyId)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateRefresh(bool : Boolean) {
|
||||
this.refreshStateState.update { bool }
|
||||
}
|
||||
|
||||
fun updateShowDialog(bool : Boolean) {
|
||||
this.showDialogState.update { bool }
|
||||
}
|
||||
|
||||
fun Launchedlobby(lobbyId : Int?): Boolean{
|
||||
return ControllerLobby.lobbyIsLaunched(lobbyId)
|
||||
}
|
||||
|
||||
fun getNbPlayerInLobby(lobbyId : Int?) : Int{
|
||||
return ControllerLobby.getNbPlayerInLobby(lobbyId)
|
||||
}
|
||||
|
||||
fun getChapterNameById(chapterId: Int?): String?{
|
||||
return ControllerChapter.getChapterNameById(chapterId)
|
||||
}
|
||||
|
||||
fun updateLobbyLauched(lobbyId : Int?,formDataBuilder: MultipartBody.Builder){
|
||||
ControllerLobby.updateLobbyLauched(lobbyId,formDataBuilder)
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
After Width: | Height: | Size: 22 KiB |
@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
After Width: | Height: | Size: 152 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 982 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 7.6 KiB |
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">MathsEduc</string>
|
||||
</resources>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.MathsEduc" parent="android:Theme.Material.Light.NoActionBar">
|
||||
<item name="android:windowBackground">@drawable/background</item>
|
||||
</style>
|
||||
</resources>
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
@ -0,0 +1,17 @@
|
||||
package com.example.mathseduc
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
@ -0,0 +1,6 @@
|
||||
#Wed Mar 06 13:56:05 CET 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@ -0,0 +1,18 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "MathsEduc"
|
||||
include(":app")
|
||||
|
@ -0,0 +1,14 @@
|
||||
<CascadingBlazoredModal>
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found </PageTitle>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</CascadingBlazoredModal>
|
@ -0,0 +1 @@
|
||||
<button type="button" class="btn btn-primary mb-2" @onclick="Back">@Localizer["Back"]</button>
|
@ -0,0 +1,29 @@
|
||||
<BackButton RedirectionPage="/questions"></BackButton>
|
||||
|
||||
<div class="text-center pb-2">
|
||||
<h3>Question n°@Question.Id</h3>
|
||||
</div>
|
||||
<div class="text-center pb-5 mt-3">
|
||||
<h5>@Question.Content</h5>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid text-center justify-content-center row card-body">
|
||||
@foreach (var answer in Answers)
|
||||
{
|
||||
<div class="col-3 text-center">
|
||||
@if (answer.Id == Question.IdAnswerGood)
|
||||
{
|
||||
<p class="text-success"><strong>@answer.Content</strong></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-danger">@answer.Content</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center text-muted">
|
||||
Chapitre : @Question.ChapterName
|
||||
</div>
|
||||
|
@ -0,0 +1,12 @@
|
||||
@page "/DeleteConfirmation"
|
||||
|
||||
<div class="simple-form">
|
||||
|
||||
<p>
|
||||
@Localizer["Question"]
|
||||
</p>
|
||||
|
||||
<button @onclick="ConfirmDelete" class="btn btn-primary">@Localizer["Delete"]</button>
|
||||
|
||||
<button @onclick="Cancel" class="btn btn-secondary">@Localizer["Cancel"]</button>
|
||||
</div>
|
@ -0,0 +1,7 @@
|
||||
namespace Blazor.Pages;
|
||||
|
||||
public static class API
|
||||
{
|
||||
public static string TOKEN = "qUOGkWdoPCgbmuqxIC8xiaX0rV1Pw1LoPafkaoHOgszEyD9P2vcOu493xCDZpAqO"; //ASK IF YOU WANT THOSE ACCESS
|
||||
public static string API_URL = "https://trusting-panini.87-106-126-109.plesk.page/api/"; //ASK IF YOU WANT THOSE ACCESS
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
@page "/addAdministrator"
|
||||
@using Blazor.Models
|
||||
@using Blazor.Components
|
||||
<PageTitle>@Localizer["PageTitle"]</PageTitle>
|
||||
<BackButton RedirectionPage="/administrators"></BackButton>
|
||||
|
||||
<div class="container text-center">
|
||||
<div class="border border-dark p-4 d-inline-block">
|
||||
<h3>@Localizer["Title"]</h3>
|
||||
<EditForm Model="@administratorModel" OnValidSubmit="@HandleValidSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-5 text-end">
|
||||
<label for="username" class="col-form-label">@Localizer["Username"] :</label>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<InputText id="username" @bind-Value="administratorModel.Username" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-5 text-end">
|
||||
<label for="hashedPassword" class="col-form-label">@Localizer["Password"] :</label>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<InputText id="hashedPassword" @bind-Value="administratorModel.HashedPassword" class="form-control" type="password"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-success mb-2">@Localizer["Submit"]</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,28 @@
|
||||
@page "/administrators"
|
||||
@using Blazorise.DataGrid
|
||||
@using Blazor.ViewClasses
|
||||
<PageTitle>@Localizer["PageTitle"]</PageTitle>
|
||||
<h3>@Localizer["Title"]</h3>
|
||||
|
||||
<div>
|
||||
<NavLink class="btn btn-primary" href="addAdministrator" Match="NavLinkMatch.All">
|
||||
<i class="fa fa-plus"></i> @Localizer["Add"]
|
||||
</NavLink>
|
||||
</div>
|
||||
<DataGrid TItem="Administrator"
|
||||
Data="@administrators"
|
||||
ReadData="@OnReadData"
|
||||
TotalItems="@totalItem"
|
||||
PageSize="10"
|
||||
ShowPager
|
||||
Responsive>
|
||||
<DataGridColumn TItem="Administrator" Field="@nameof(Administrator.Id)" Caption="#" />
|
||||
<DataGridColumn TItem="Administrator" Field="@nameof(Administrator.Username)" Caption="@Localizer["Username"]" />
|
||||
<DataGridColumn TItem="Administrator" Field="@nameof(Administrator.Id)" Caption="Action">
|
||||
<DisplayTemplate>
|
||||
<a href="editAdministrator/@(context.Id)" class="btn btn-primary"><i class="fa fa-edit"></i> @Localizer["Edit"]</a>
|
||||
<button type="button" class="btn btn-danger" @onclick="() => OnDelete(context.Id)"><i class="fa fa-trash"></i> @Localizer["Delete"]</button>
|
||||
</DisplayTemplate>
|
||||
</DataGridColumn>
|
||||
</DataGrid>
|
||||
|
@ -0,0 +1,35 @@
|
||||
@page "/editAdministrator/{Id:int}"
|
||||
|
||||
@using Blazor.Components;
|
||||
<PageTitle>@Localizer["PageTitle"]</PageTitle>
|
||||
<BackButton RedirectionPage="/administrators"></BackButton>
|
||||
|
||||
<div class="container text-center">
|
||||
<div class="border border-dark p-4 d-inline-block">
|
||||
<h3>@Localizer["Title"]</h3>
|
||||
|
||||
<EditForm Model="@administratorModel" OnValidSubmit="@HandleValidSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-5 text-end">
|
||||
<label for="username" class="col-form-label">@Localizer["Username"] :</label>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<InputText id="username" @bind-Value="administratorModel.Username" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-5 text-end">
|
||||
<label for="hashedPassword" class="col-form-label">@Localizer["HashedPassword"] :</label>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<InputText id="hashedPassword" @bind-Value="administratorModel.HashedPassword" class="form-control" type="password"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mb-2">@Localizer["Submit"]</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,29 @@
|
||||
@page "/addChapter"
|
||||
@using Blazor.Models
|
||||
@using Blazor.Components
|
||||
<PageTitle>@Localizer["PageTitle"]</PageTitle>
|
||||
<BackButton RedirectionPage="/chapters"></BackButton>
|
||||
|
||||
<div class="container text-center">
|
||||
<div class="border border-dark p-4 d-inline-block">
|
||||
<h3>@Localizer["Title"]</h3>
|
||||
|
||||
|
||||
<EditForm Model="@chapterModel" OnValidSubmit="@HandleValidSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-4 text-end">
|
||||
<label for="name" class="col-form-label">@Localizer["Name"] :</label>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<InputText id="name" @bind-Value="chapterModel.Name" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-success mb-2">@Localizer["Submit"]</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,28 @@
|
||||
@page "/chapters"
|
||||
@using Blazor.ViewClasses;
|
||||
@using Blazorise.DataGrid
|
||||
@using Blazored.Modal;
|
||||
<PageTitle>@Localizer["PageTitle"]</PageTitle>
|
||||
<h3>@Localizer["Title"]</h3>
|
||||
<div>
|
||||
<NavLink class="btn btn-primary" href="addChapter" Match="NavLinkMatch.All">
|
||||
<i class="fa fa-plus"></i> @Localizer["Add"]
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<DataGrid TItem="Chapter"
|
||||
Data="@chapters"
|
||||
ReadData="@OnReadData"
|
||||
TotalItems="@totalChapter"
|
||||
PageSize="10"
|
||||
ShowPager
|
||||
Responsive>
|
||||
<DataGridColumn TItem="Chapter" Field="@nameof(Chapter.Id)" Caption="#" />
|
||||
<DataGridColumn TItem="Chapter" Field="@nameof(Chapter.Name)" Caption="@Localizer["Name"]" />
|
||||
<DataGridColumn TItem="Chapter" Field="@nameof(Chapter.Id)" Caption="Action">
|
||||
<DisplayTemplate>
|
||||
<a href="editChapter/@(context.Id)" class="btn btn-primary"><i class="fa fa-edit"></i> @Localizer["Edit"]</a>
|
||||
<button type="button" class="btn btn-danger" @onclick="() => OnDelete(context.Id)"><i class="fa fa-trash"></i> @Localizer["Delete"]</button>
|
||||
</DisplayTemplate>
|
||||
</DataGridColumn>
|
||||
</DataGrid>
|
@ -0,0 +1,29 @@
|
||||
@page "/editChapter/{Id:int}"
|
||||
|
||||
@using Blazor.Components;
|
||||
<PageTitle>@Localizer["PageTitle"]</PageTitle>
|
||||
<BackButton RedirectionPage="/chapters"></BackButton>
|
||||
|
||||
|
||||
<div class="container text-center">
|
||||
<div class="border border-dark p-4 d-inline-block">
|
||||
<h3>@Localizer["Title"]</h3>
|
||||
<EditForm Model="@chapterModel" OnValidSubmit="@HandleValidSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-4 text-end">
|
||||
<label for="name" class="col-form-label">@Localizer["Name"] : </label>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<InputText id="name" @bind-Value="chapterModel.Name" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success mb-2">@Localizer["Submit"]</button>
|
||||
</EditForm>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
@page
|
||||
@model Blazor.Pages.ErrorModel
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<PageTitle>Error</PageTitle>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<title>Error</title>
|
||||
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="content px-4">
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (Model.ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@Model.RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,13 @@
|
||||
@page "/"
|
||||
|
||||
<PageTitle>@Localizer["PageTitle"]</PageTitle>
|
||||
|
||||
<div class="container text-center mb-5">
|
||||
<img src="/images/mathseduc.png" alt="Logo MathsEduc" height="150" />
|
||||
</div>
|
||||
|
||||
<h1 class="text-center">@Localizer["Title"]</h1>
|
||||
|
||||
<p class="text-center">@Localizer["Text"]</p>
|
||||
|
||||
<button type="button" class="btn btn-primary mb-2" @onclick="CloseApplication">Fermer l'application</button>
|