Implemented Connection with Wi-Fi Direct and IP #9
25 changed files with 371 additions and 141 deletions
|
@ -1,5 +1,6 @@
|
|||
package com.faraphel.tasks_valider.connectivity.task
|
||||
|
||||
import com.faraphel.tasks_valider.connectivity.task.api.TaskSessionManagerClientApi
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.*
|
||||
|
||||
|
@ -17,13 +18,19 @@ class TaskClient(
|
|||
) {
|
||||
private val httpClient = TaskEntityHttpClient(address, port, baseCookies)
|
||||
|
||||
val clientApi = ClassClientApi(httpClient)
|
||||
val personApi = PersonClientApi(httpClient)
|
||||
val sessionApi = SessionClientApi(httpClient)
|
||||
val subjectApi = SubjectClientApi(httpClient)
|
||||
val taskApi = TaskClientApi(httpClient)
|
||||
val validationApi = ValidationClientApi(httpClient)
|
||||
// all the entities API
|
||||
class Entities(httpClient: TaskEntityHttpClient) {
|
||||
val client = ClassClientApi(httpClient)
|
||||
val person = PersonClientApi(httpClient)
|
||||
val session = SessionClientApi(httpClient)
|
||||
val subject = SubjectClientApi(httpClient)
|
||||
val task = TaskClientApi(httpClient)
|
||||
val validation = ValidationClientApi(httpClient)
|
||||
|
||||
val relationClassPersonApi = RelationClassPersonClientApi(httpClient)
|
||||
val relationPersonSessionSubjectApi = RelationPersonSessionSubjectClientApi(httpClient)
|
||||
val relationClassPerson = RelationClassPersonClientApi(httpClient)
|
||||
val relationPersonSessionSubject = RelationPersonSessionSubjectClientApi(httpClient)
|
||||
}
|
||||
val entities = Entities(httpClient)
|
||||
|
||||
val session = TaskSessionManagerClientApi(httpClient)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.faraphel.tasks_valider.connectivity.task
|
||||
|
||||
import com.faraphel.tasks_valider.connectivity.task.api.TaskSessionManagerApi
|
||||
import com.faraphel.tasks_valider.connectivity.task.api.TaskSessionManagerServerApi
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSessionManager
|
||||
import com.faraphel.tasks_valider.database.TaskDatabase
|
||||
import com.faraphel.tasks_valider.database.api.server.DatabaseApi
|
||||
|
@ -24,7 +24,7 @@ class TaskServer(
|
|||
) : NanoHTTPD(port) {
|
||||
private val sessionManager = TaskSessionManager(adminPersonEntity) ///< the session manager
|
||||
private val databaseApi = DatabaseApi(this.database, session) ///< the api of the database
|
||||
private val sessionManagerApi = TaskSessionManagerApi(this.sessionManager, this.database) ///< the api of the session manager
|
||||
private val sessionManagerApi = TaskSessionManagerServerApi(this.sessionManager, this.database) ///< the api of the session manager
|
||||
|
||||
/**
|
||||
* Get the admin person entity
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package com.faraphel.tasks_valider.connectivity.task.api
|
||||
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSession
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.entities.SessionEntity
|
||||
import com.faraphel.tasks_valider.database.entities.error.HttpException
|
||||
import com.faraphel.tasks_valider.utils.parser
|
||||
|
||||
|
||||
/**
|
||||
* Interface to communicate with the server session manager
|
||||
*/
|
||||
class TaskSessionManagerClientApi(private val client: TaskEntityHttpClient) {
|
||||
/**
|
||||
* Create a new session
|
||||
* @param cardId the id of the user's card
|
||||
* @param password the password of the user
|
||||
*/
|
||||
fun newFromCardId(cardId: String, password: String): TaskSession {
|
||||
val response = this.client.post(
|
||||
"sessions/self",
|
||||
parser.toJson(mapOf(
|
||||
"card_id" to cardId,
|
||||
"password" to password
|
||||
))
|
||||
)
|
||||
|
||||
// in case of error, notify it
|
||||
if (!response.isSuccessful)
|
||||
throw HttpException(response.code)
|
||||
|
||||
val data = response.body.string()
|
||||
|
||||
// parse the result
|
||||
return parser.fromJson(
|
||||
data,
|
||||
TaskSession::class.java
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current session
|
||||
*/
|
||||
fun getSelf(): TaskSession {
|
||||
val response = this.client.get("sessions/self")
|
||||
|
||||
// in case of error, notify it
|
||||
if (!response.isSuccessful)
|
||||
throw HttpException(response.code)
|
||||
|
||||
// parse the result
|
||||
return parser.fromJson(
|
||||
response.body.string(),
|
||||
TaskSession::class.java
|
||||
)
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ import com.faraphel.tasks_valider.connectivity.task.session.TaskPermission
|
|||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSession
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSessionManager
|
||||
import com.faraphel.tasks_valider.database.TaskDatabase
|
||||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
import com.faraphel.tasks_valider.utils.getBody
|
||||
import com.faraphel.tasks_valider.utils.parser
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
|
@ -12,7 +14,7 @@ import fi.iki.elonen.NanoHTTPD
|
|||
/**
|
||||
* the HTTP API for the session manager
|
||||
*/
|
||||
class TaskSessionManagerApi(
|
||||
class TaskSessionManagerServerApi(
|
||||
private val sessionManager: TaskSessionManager,
|
||||
private val database: TaskDatabase
|
||||
) {
|
||||
|
@ -72,18 +74,14 @@ class TaskSessionManagerApi(
|
|||
NanoHTTPD.Method.POST -> {
|
||||
// get the user identifiers
|
||||
val identifiers: Map<String, String> = parser.fromJson(
|
||||
httpSession.inputStream.bufferedReader().readText(),
|
||||
httpSession.getBody(),
|
||||
object : TypeToken<Map<String, String>>() {}.type
|
||||
)
|
||||
|
||||
// check for the id
|
||||
if (!identifiers.contains("id"))
|
||||
return NanoHTTPD.newFixedLengthResponse(
|
||||
NanoHTTPD.Response.Status.BAD_REQUEST,
|
||||
"text/plain",
|
||||
"Missing id"
|
||||
)
|
||||
val person: PersonEntity
|
||||
|
||||
// check for the id
|
||||
if (identifiers.contains("id")) {
|
||||
// get the id of the user (if invalid, return an error)
|
||||
val personId: Long = identifiers["id"]!!.toLongOrNull()
|
||||
?: return NanoHTTPD.newFixedLengthResponse(
|
||||
|
@ -92,6 +90,29 @@ class TaskSessionManagerApi(
|
|||
"Invalid id"
|
||||
)
|
||||
|
||||
// check if the identifiers are correct
|
||||
person = this.database.personDao().getById(personId)
|
||||
?: return NanoHTTPD.newFixedLengthResponse(
|
||||
NanoHTTPD.Response.Status.BAD_REQUEST,
|
||||
"text/plain",
|
||||
"No person with this id"
|
||||
)
|
||||
} else if (identifiers.contains("card_id")) {
|
||||
// check if the identifiers are correct
|
||||
person = this.database.personDao().getByCardId(identifiers["card_id"]!!)
|
||||
?: return NanoHTTPD.newFixedLengthResponse(
|
||||
NanoHTTPD.Response.Status.BAD_REQUEST,
|
||||
"text/plain",
|
||||
"No person with this id"
|
||||
)
|
||||
} else {
|
||||
return NanoHTTPD.newFixedLengthResponse(
|
||||
NanoHTTPD.Response.Status.BAD_REQUEST,
|
||||
"text/plain",
|
||||
"Missing id or card_id"
|
||||
)
|
||||
}
|
||||
|
||||
// check for the password
|
||||
if (!identifiers.contains("password"))
|
||||
return NanoHTTPD.newFixedLengthResponse(
|
||||
|
@ -100,14 +121,6 @@ class TaskSessionManagerApi(
|
|||
"Missing password"
|
||||
)
|
||||
|
||||
// check if the identifiers are correct
|
||||
val person = this.database.personDao().getById(personId)
|
||||
?: return NanoHTTPD.newFixedLengthResponse(
|
||||
NanoHTTPD.Response.Status.BAD_REQUEST,
|
||||
"text/plain",
|
||||
"No person with this id"
|
||||
)
|
||||
|
||||
// check if the password is correct
|
||||
if (!person.checkPassword(identifiers["password"]!!))
|
||||
return NanoHTTPD.newFixedLengthResponse(
|
||||
|
@ -119,11 +132,11 @@ class TaskSessionManagerApi(
|
|||
// create a new session for the userJHH
|
||||
val (sessionToken, session) = this.sessionManager.newSessionData(person)
|
||||
|
||||
// create the response
|
||||
// create the response with the session data
|
||||
val response = NanoHTTPD.newFixedLengthResponse(
|
||||
NanoHTTPD.Response.Status.OK,
|
||||
"text/plain",
|
||||
"Session updated"
|
||||
"application/json",
|
||||
parser.toJson(session)
|
||||
)
|
||||
|
||||
// set the session token in the cookies
|
|
@ -1,6 +1,7 @@
|
|||
package com.faraphel.tasks_valider.connectivity.task.session
|
||||
|
||||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
|
@ -9,5 +10,5 @@ import kotlinx.serialization.Serializable
|
|||
*/
|
||||
@Serializable
|
||||
data class TaskSession(
|
||||
val person: PersonEntity,
|
||||
@Expose val person: PersonEntity,
|
||||
)
|
|
@ -18,6 +18,12 @@ interface PersonDao : BaseTaskDao<PersonEntity> {
|
|||
@Query("SELECT * FROM ${PersonEntity.TABLE_NAME} WHERE id = :id")
|
||||
fun getById(id: Long): PersonEntity?
|
||||
|
||||
/**
|
||||
* Get the object from its card identifier
|
||||
*/
|
||||
@Query("SELECT * FROM ${PersonEntity.TABLE_NAME} WHERE card_id = :cardId")
|
||||
fun getByCardId(cardId: String): PersonEntity?
|
||||
|
||||
@Query(
|
||||
"SELECT * FROM ${PersonEntity.TABLE_NAME} " +
|
||||
"WHERE id IN (" +
|
||||
|
|
|
@ -4,12 +4,13 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
|
||||
|
||||
@Entity(tableName = ClassEntity.TABLE_NAME)
|
||||
data class ClassEntity (
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
@ColumnInfo("name") val name: String,
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) @Expose val id: Long = 0,
|
||||
@ColumnInfo("name") @Expose val name: String,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "classes"
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.room.Entity
|
|||
import androidx.room.PrimaryKey
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskRole
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
@ -12,12 +13,13 @@ import java.util.*
|
|||
@Serializable
|
||||
@Entity(tableName = PersonEntity.TABLE_NAME)
|
||||
data class PersonEntity (
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
@ColumnInfo("first_name") val firstName: String,
|
||||
@ColumnInfo("last_name") val lastName: String,
|
||||
@ColumnInfo("card_id") val cardId: String? = null,
|
||||
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) @Expose val id: Long = 0,
|
||||
@ColumnInfo("first_name") @Expose val firstName: String,
|
||||
@ColumnInfo("last_name") @Expose val lastName: String,
|
||||
@ColumnInfo("card_id") @Expose val cardId: String? = null,
|
||||
@ColumnInfo("password_hash") val passwordHash: String? = null,
|
||||
@ColumnInfo("role") val role: TaskRole = TaskRole.STUDENT,
|
||||
@ColumnInfo("role") @Expose val role: TaskRole = TaskRole.STUDENT,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "persons"
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
|
||||
@Entity(
|
||||
tableName = RelationClassPersonEntity.TABLE_NAME,
|
||||
|
@ -27,8 +28,8 @@ import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
|||
]
|
||||
)
|
||||
data class RelationClassPersonEntity (
|
||||
@ColumnInfo("student_id", index = true) val studentId: Long,
|
||||
@ColumnInfo("class_id", index = true) val classId: Long,
|
||||
@ColumnInfo("student_id", index = true) @Expose val studentId: Long,
|
||||
@ColumnInfo("class_id", index = true) @Expose val classId: Long,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "relation_class_person"
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
|
||||
|
||||
/**
|
||||
|
@ -38,9 +39,9 @@ import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
|||
]
|
||||
)
|
||||
data class RelationPersonSessionSubjectEntity (
|
||||
@ColumnInfo("student_id", index = true) val studentId: Long,
|
||||
@ColumnInfo("session_id", index = true) val sessionId: Long,
|
||||
@ColumnInfo("subject_id", index = true) val subjectId: Long,
|
||||
@ColumnInfo("student_id", index = true) @Expose val studentId: Long,
|
||||
@ColumnInfo("session_id", index = true) @Expose val sessionId: Long,
|
||||
@ColumnInfo("subject_id", index = true) @Expose val subjectId: Long,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "relation_person_session_subject"
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.room.Entity
|
|||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
import java.time.Instant
|
||||
|
||||
@Entity(
|
||||
|
@ -19,9 +20,9 @@ import java.time.Instant
|
|||
]
|
||||
)
|
||||
data class SessionEntity (
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
@ColumnInfo("name") val name: String? = null,
|
||||
@ColumnInfo("start") val start: Instant,
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) @Expose val id: Long = 0,
|
||||
@ColumnInfo("name") @Expose val name: String? = null,
|
||||
@ColumnInfo("start") @Expose val start: Instant,
|
||||
|
||||
@ColumnInfo("class_id", index = true) val classId: Long? = null,
|
||||
) : BaseEntity() {
|
||||
|
|
|
@ -4,11 +4,12 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
|
||||
@Entity(tableName = SubjectEntity.TABLE_NAME)
|
||||
data class SubjectEntity (
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
@ColumnInfo("name") val name: String,
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) @Expose val id: Long = 0,
|
||||
@ColumnInfo("name") @Expose val name: String,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "subjects"
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.room.Entity
|
|||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
|
||||
@Entity(
|
||||
tableName = TaskEntity.TABLE_NAME,
|
||||
|
@ -18,12 +19,12 @@ import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
|||
]
|
||||
)
|
||||
data class TaskEntity (
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
@ColumnInfo("title") val title: String,
|
||||
@ColumnInfo("description") val description: String? = null,
|
||||
@ColumnInfo("order") val order: Int, ///< the order of the task in the list of the subject
|
||||
@ColumnInfo("id") @PrimaryKey(autoGenerate = true) @Expose val id: Long = 0,
|
||||
@ColumnInfo("title") @Expose val title: String,
|
||||
@ColumnInfo("description") @Expose val description: String? = null,
|
||||
@ColumnInfo("order") @Expose val order: Int, ///< the order of the task in the list of the subject
|
||||
|
||||
@ColumnInfo("subject_id", index = true) val subjectId: Long,
|
||||
@ColumnInfo("subject_id", index = true) @Expose val subjectId: Long,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "tasks"
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.google.gson.annotations.Expose
|
||||
import java.time.Instant
|
||||
|
||||
@Entity(
|
||||
|
@ -35,11 +36,11 @@ import java.time.Instant
|
|||
]
|
||||
)
|
||||
data class ValidationEntity (
|
||||
@ColumnInfo("teacher_id", index = true) val teacherId: Long,
|
||||
@ColumnInfo("student_id", index = true) val studentId: Long,
|
||||
@ColumnInfo("task_id", index = true) val taskId: Long,
|
||||
@ColumnInfo("teacher_id", index = true) @Expose val teacherId: Long,
|
||||
@ColumnInfo("student_id", index = true) @Expose val studentId: Long,
|
||||
@ColumnInfo("task_id", index = true) @Expose val taskId: Long,
|
||||
|
||||
@ColumnInfo("date") val date: Instant,
|
||||
@ColumnInfo("date") @Expose val date: Instant,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "validations"
|
||||
|
|
|
@ -3,4 +3,8 @@ package com.faraphel.tasks_valider.database.entities.error
|
|||
|
||||
class HttpException(
|
||||
private val code: Int,
|
||||
) : Exception("Http Exception: $code")
|
||||
) : Exception("Http Exception: $code") {
|
||||
fun getCode(): Int {
|
||||
return code
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.authentication
|
||||
|
||||
import android.app.Activity
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.faraphel.tasks_valider.connectivity.task.TaskClient
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSession
|
||||
import com.faraphel.tasks_valider.database.entities.error.HttpException
|
||||
import com.faraphel.tasks_valider.ui.screen.scan.qr.ScanBarcodeScreen
|
||||
import com.journeyapps.barcodescanner.BarcodeResult
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
|
||||
/**
|
||||
* Authentification screen where the client can give his information
|
||||
*/
|
||||
@Composable
|
||||
fun AuthenticationClientScreen(activity: Activity, client: TaskClient, session: MutableState<TaskSession?>) {
|
||||
val controller = rememberNavController()
|
||||
val barcode = remember { mutableStateOf<BarcodeResult?>(null) }
|
||||
|
||||
NavHost(navController = controller, startDestination = "main") {
|
||||
composable("main") {
|
||||
AuthenticationClientContent(activity, controller, client, session, barcode)
|
||||
}
|
||||
composable("scan") {
|
||||
if (barcode.value == null) ScanBarcodeScreen(activity, barcode)
|
||||
else controller.navigate("main")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun AuthenticationClientContent(
|
||||
activity: Activity,
|
||||
controller: NavController,
|
||||
client: TaskClient,
|
||||
session: MutableState<TaskSession?>,
|
||||
barcode: MutableState<BarcodeResult?>,
|
||||
) {
|
||||
val cardId = remember { mutableStateOf("") }
|
||||
val password = remember { mutableStateOf("") }
|
||||
|
||||
// check if the barcode contain information about the card
|
||||
|
||||
var defaultCardId = ""
|
||||
if (barcode.value != null) {
|
||||
val studentUrl = barcode.value!!.text.toHttpUrl()
|
||||
cardId.value = studentUrl.pathSegments[0]
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
// title
|
||||
Text(
|
||||
text = "Authentication",
|
||||
fontSize = 32.sp
|
||||
)
|
||||
|
||||
// separator
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// card identifier
|
||||
Row {
|
||||
// text
|
||||
TextField(
|
||||
value = cardId.value,
|
||||
placeholder = { Text("Card Id") },
|
||||
onValueChange = { text -> cardId.value = text },
|
||||
)
|
||||
// button to scan the card
|
||||
Button(onClick = {
|
||||
barcode.value = null
|
||||
controller.navigate("scan")
|
||||
}) {
|
||||
Text("Scan")
|
||||
}
|
||||
}
|
||||
|
||||
// password
|
||||
TextField(
|
||||
value = password.value,
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
placeholder = { Text("Password") },
|
||||
onValueChange = { text -> password.value = text },
|
||||
)
|
||||
|
||||
// separator
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// submit button
|
||||
|
||||
Button(onClick = {
|
||||
Thread { authenticate(activity, client, cardId.value, password.value, session) }.start()
|
||||
}) {
|
||||
Text("Submit")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun authenticate(
|
||||
activity: Activity,
|
||||
client: TaskClient,
|
||||
cardId: String,
|
||||
password: String,
|
||||
session: MutableState<TaskSession?>
|
||||
) {
|
||||
try {
|
||||
// try to get a session from the identifiers
|
||||
session.value = client.session.newFromCardId(cardId, password)
|
||||
} catch (exception: HttpException) {
|
||||
// in case of error, show a message to the user
|
||||
when (exception.getCode()) {
|
||||
401 -> {
|
||||
activity.runOnUiThread {
|
||||
Toast.makeText(activity, "Invalid card id or password", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
activity.runOnUiThread {
|
||||
Toast.makeText(activity, "Unknown error: $exception", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.authentification
|
||||
package com.faraphel.tasks_valider.ui.screen.authentication
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Button
|
||||
|
@ -20,7 +20,7 @@ import com.faraphel.tasks_valider.database.entities.PersonEntity
|
|||
* Authentification screen where the host can give his information
|
||||
*/
|
||||
@Composable
|
||||
fun AuthentificationServerScreen(personEntity: MutableState<PersonEntity?>) {
|
||||
fun AuthenticationServerScreen(personEntity: MutableState<PersonEntity?>) {
|
||||
val firstName = remember { mutableStateOf("") }
|
||||
val lastName = remember { mutableStateOf("") }
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.authentification
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
|
||||
|
||||
/**
|
||||
* Authentification screen where the client can give his information
|
||||
*/
|
||||
@Composable
|
||||
fun AuthentificationClientScreen() {
|
||||
val cardId = remember { mutableStateOf("") }
|
||||
val password = remember { mutableStateOf("") }
|
||||
|
||||
Column {
|
||||
// card identifier
|
||||
TextField(
|
||||
value = cardId.value,
|
||||
onValueChange = { text -> cardId.value = text },
|
||||
)
|
||||
|
||||
// password
|
||||
TextField(
|
||||
value = password.value,
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
onValueChange = { text -> password.value = text },
|
||||
)
|
||||
|
||||
// button
|
||||
Button(onClick = { /* do something */ }) {
|
||||
Text("Submit")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.communication.internet
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
|
@ -13,21 +11,38 @@ import androidx.compose.runtime.MutableState
|
|||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.faraphel.tasks_valider.connectivity.task.TaskClient
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSession
|
||||
import com.faraphel.tasks_valider.ui.screen.authentication.AuthenticationClientScreen
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.DEFAULT_SERVER_ADDRESS
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.DEFAULT_SERVER_PORT
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.RANGE_SERVER_PORT
|
||||
import com.faraphel.tasks_valider.ui.screen.task.TaskSessionController
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@Composable
|
||||
fun CommunicationInternetClientScreen(activity: Activity) {
|
||||
val client = remember { mutableStateOf<TaskClient?>(null) }
|
||||
val session = remember { mutableStateOf<TaskSession?>(null) }
|
||||
|
||||
// TODO(Faraphel): fix and get a user
|
||||
// if (client.value == null) CommunicationInternetClientContent(client)
|
||||
// else TaskSessionScreen(activity, client.value!!, user)
|
||||
// if the client is not connected, show the connection screen
|
||||
if (client.value == null)
|
||||
return CommunicationInternetClientContent(client)
|
||||
// if the user is not authenticated, show the authentication screen
|
||||
if (session.value == null)
|
||||
return AuthenticationClientScreen(activity, client.value!!, session)
|
||||
|
||||
// show the main screen
|
||||
TaskSessionController(
|
||||
activity,
|
||||
client.value!!,
|
||||
session.value!!.person,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
@ -36,18 +51,31 @@ fun CommunicationInternetClientContent(client: MutableState<TaskClient?>) {
|
|||
val serverAddress = remember { mutableStateOf(DEFAULT_SERVER_ADDRESS) }
|
||||
val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) }
|
||||
|
||||
Column {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
// title
|
||||
Text(
|
||||
text = "Connection",
|
||||
fontSize = 32.sp
|
||||
)
|
||||
|
||||
// separator
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// server address
|
||||
TextField(
|
||||
value = serverAddress.value,
|
||||
onValueChange = { text ->
|
||||
serverAddress.value = text
|
||||
}
|
||||
placeholder = { Text("server IP") },
|
||||
onValueChange = { text -> serverAddress.value = text }
|
||||
)
|
||||
|
||||
// server port
|
||||
TextField(
|
||||
value = serverPort.intValue.toString(),
|
||||
placeholder = { Text("server port") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
onValueChange = { text ->
|
||||
val port = text.toInt()
|
||||
|
@ -57,6 +85,10 @@ fun CommunicationInternetClientContent(client: MutableState<TaskClient?>) {
|
|||
}
|
||||
)
|
||||
|
||||
// separator
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
// connect button
|
||||
Button(onClick = {
|
||||
// TODO(Faraphel): check if the server is reachable
|
||||
client.value = TaskClient(serverAddress.value, serverPort.intValue)
|
||||
|
|
|
@ -27,7 +27,7 @@ import com.faraphel.tasks_valider.database.entities.ClassEntity
|
|||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
import com.faraphel.tasks_valider.database.entities.SessionEntity
|
||||
import com.faraphel.tasks_valider.database.populateSubjectSessionPersonTest
|
||||
import com.faraphel.tasks_valider.ui.screen.authentification.AuthentificationServerScreen
|
||||
import com.faraphel.tasks_valider.ui.screen.authentication.AuthenticationServerScreen
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.DEFAULT_SERVER_PORT
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.RANGE_SERVER_PORT
|
||||
import com.faraphel.tasks_valider.ui.screen.task.TaskSessionController
|
||||
|
@ -37,7 +37,6 @@ import java.time.Instant
|
|||
/**
|
||||
* Screen for the host to configure the server
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@Composable
|
||||
fun CommunicationInternetServerScreen(
|
||||
activity: Activity,
|
||||
|
@ -52,7 +51,7 @@ fun CommunicationInternetServerScreen(
|
|||
NavHost(navController = controller, startDestination = "authentication") {
|
||||
composable("authentication") {
|
||||
// if the admin person is not created, prompt the user for the admin information
|
||||
if (adminPersonEntityRaw.value == null) AuthentificationServerScreen(adminPersonEntityRaw)
|
||||
if (adminPersonEntityRaw.value == null) AuthenticationServerScreen(adminPersonEntityRaw)
|
||||
else controller.navigate("configuration")
|
||||
}
|
||||
composable("configuration") {
|
||||
|
|
|
@ -33,7 +33,6 @@ import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.CommunicationW
|
|||
* @param activity: The activity that hosts the communication screen.
|
||||
* @param database: the database.
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@Composable
|
||||
fun CommunicationModeSelectionScreen(activity: Activity, database: TaskDatabase) {
|
||||
val controller = rememberNavController()
|
||||
|
|
|
@ -66,7 +66,7 @@ fun quickValidation(
|
|||
}
|
||||
|
||||
// requests all the persons
|
||||
val allPersons = client.personApi.getAll()
|
||||
val allPersons = client.entities.person.getAll()
|
||||
// get the person with the matching card
|
||||
val person = allPersons.firstOrNull { person -> person.cardId == cardId }
|
||||
|
||||
|
@ -79,19 +79,19 @@ fun quickValidation(
|
|||
}
|
||||
|
||||
// requests all the relation persons - subjects
|
||||
val allRelationsPersonSubject = client.relationPersonSessionSubjectApi.getAll()
|
||||
val allRelationsPersonSubject = client.entities.relationPersonSessionSubject.getAll()
|
||||
// get the corresponding relation
|
||||
val relationPersonSubject = allRelationsPersonSubject.first { relation -> relation.studentId == person.id }
|
||||
|
||||
// requests all the tasks
|
||||
val allTasks = client.taskApi.getAll()
|
||||
val allTasks = client.entities.task.getAll()
|
||||
// get the corresponding tasks
|
||||
val tasks = allTasks
|
||||
.filter { task -> task.subjectId == relationPersonSubject.subjectId }
|
||||
.sortedBy { task -> task.order }
|
||||
|
||||
// requests all the validations
|
||||
val allValidations = client.validationApi.getAll()
|
||||
val allValidations = client.entities.validation.getAll()
|
||||
// get the corresponding relation
|
||||
val validations = allValidations.filter { validation -> validation.studentId == person.id }
|
||||
|
||||
|
@ -112,7 +112,7 @@ fun quickValidation(
|
|||
}
|
||||
|
||||
// create a new validation on the server
|
||||
client.validationApi.save(
|
||||
client.entities.validation.save(
|
||||
ValidationEntity(
|
||||
teacherId=user.id,
|
||||
studentId=person.id,
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.task
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Activity.RESULT_OK
|
||||
import android.content.Intent
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
|
@ -125,7 +120,7 @@ fun refreshStudents(
|
|||
) {
|
||||
try {
|
||||
// try to get all the persons in that session
|
||||
students.value = client.personApi.getAll()
|
||||
students.value = client.entities.person.getAll()
|
||||
} catch (exception: Exception) {
|
||||
// in case of error, show a message
|
||||
return activity.runOnUiThread {
|
||||
|
@ -141,12 +136,12 @@ fun exportToFile(
|
|||
client: TaskClient,
|
||||
) {
|
||||
// get all the values to export
|
||||
val allPersons = client.personApi.getAll()
|
||||
val allPersons = client.entities.person.getAll()
|
||||
val allStudents = allPersons.filter { student -> student.role == TaskRole.STUDENT }
|
||||
val allRelationsStudentSessionSubject = client.relationPersonSessionSubjectApi.getAll()
|
||||
val allSubjects = client.subjectApi.getAll()
|
||||
val allTasks = client.taskApi.getAll()
|
||||
val allValidations = client.validationApi.getAll()
|
||||
val allRelationsStudentSessionSubject = client.entities.relationPersonSessionSubject.getAll()
|
||||
val allSubjects = client.entities.subject.getAll()
|
||||
val allTasks = client.entities.task.getAll()
|
||||
val allValidations = client.entities.validation.getAll()
|
||||
|
||||
// for each student
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ fun refreshTasksValidations(
|
|||
validations: MutableState<List<ValidationEntity>?>,
|
||||
) {
|
||||
// try to obtain the list of subject
|
||||
val allRelationsPersonSessionSubject = client.relationPersonSessionSubjectApi.getAll()
|
||||
val allRelationsPersonSessionSubject = client.entities.relationPersonSessionSubject.getAll()
|
||||
// get the subject that the student is using
|
||||
val relationPersonSessionSubject = allRelationsPersonSessionSubject.firstOrNull { relation ->
|
||||
relation.studentId == student.id
|
||||
|
@ -136,7 +136,7 @@ fun refreshTasksValidations(
|
|||
return activity.runOnUiThread { Toast.makeText(activity, "No subject assigned", Toast.LENGTH_LONG).show() }
|
||||
|
||||
// try to obtain the list of tasks
|
||||
val allTasks = client.taskApi.getAll()
|
||||
val allTasks = client.entities.task.getAll()
|
||||
|
||||
// get the tasks that are linked to this subject
|
||||
tasks.value = allTasks
|
||||
|
@ -144,7 +144,7 @@ fun refreshTasksValidations(
|
|||
.sortedBy { task -> task.order }
|
||||
|
||||
// try to obtain the list of validations
|
||||
val allValidations = client.validationApi.getAll()
|
||||
val allValidations = client.entities.validation.getAll()
|
||||
// filter only the interesting validations
|
||||
validations.value = allValidations.filter { validation ->
|
||||
validation.studentId == student.id &&
|
||||
|
@ -156,8 +156,8 @@ fun refreshTasksValidations(
|
|||
fun updateValidation(client: TaskClient, checked: Boolean, validation: ValidationEntity) {
|
||||
if (checked)
|
||||
// if the validation is not set, create it
|
||||
client.validationApi.save(validation)
|
||||
client.entities.validation.save(validation)
|
||||
else
|
||||
// if the validation is set, delete it
|
||||
client.validationApi.delete(validation)
|
||||
client.entities.validation.delete(validation)
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ import java.time.Instant
|
|||
|
||||
val parser: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(Instant::class.java, InstantConverter())
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create()
|
Loading…
Reference in a new issue