diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ee6f05e..802e9a1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,7 +11,7 @@ android { defaultConfig { applicationId = "com.faraphel.tasks_valider" - minSdk = 24 + minSdk = 26 targetSdk = 34 versionCode = 1 versionName = "1.0" @@ -52,8 +52,8 @@ android { } dependencies { - implementation("androidx.core:core-ktx:1.13.0") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") + implementation("androidx.core:core-ktx:1.13.1") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.1") implementation("androidx.activity:activity-compose:1.9.0") implementation(platform("androidx.compose:compose-bom:2023.08.00")) implementation("androidx.compose.ui:ui") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e983a70..32f3c79 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskClient.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskClient.kt index b6696d7..e202317 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskClient.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskClient.kt @@ -5,6 +5,8 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.logging.HttpLoggingInterceptor +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds /** @@ -32,7 +34,7 @@ class TaskClient( this.cookies.addAll(cookies) } } - ) + ).callTimeout(30.seconds) .build() // TODO(Faraphel): automatically convert content to the correct type ? diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ClassApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ClassApi.kt index 386b757..5512313 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ClassApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ClassApi.kt @@ -1,14 +1,68 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.ClassEntity import com.faraphel.tasks_valider.database.entities.SessionEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class ClassApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) +class ClassApi(private val dao: BaseTaskDao, private val session: SessionEntity) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, ClassEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, ClassEntity::class.java) + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, ClassEntity::class.java) + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/PersonApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/PersonApi.kt index a7c9410..a6e04e2 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/PersonApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/PersonApi.kt @@ -1,14 +1,68 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.PersonEntity import com.faraphel.tasks_valider.database.entities.SessionEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class PersonApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) +class PersonApi(private val dao: BaseTaskDao, private val session: SessionEntity) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, PersonEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, PersonEntity::class.java) + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, PersonEntity::class.java) + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationClassPersonApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationClassPersonApi.kt index 308e7e5..9a4ef5a 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationClassPersonApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationClassPersonApi.kt @@ -1,15 +1,72 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.RelationClassPersonEntity import com.faraphel.tasks_valider.database.entities.SessionEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class RelationClassPersonApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) +class RelationClassPersonApi( + private val dao: BaseTaskDao, + private val session: SessionEntity +) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, RelationClassPersonEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, RelationClassPersonEntity::class.java) + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, RelationClassPersonEntity::class.java) + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationPersonSessionSubjectApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationPersonSessionSubjectApi.kt index d5bcb99..feb1a79 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationPersonSessionSubjectApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/RelationPersonSessionSubjectApi.kt @@ -1,15 +1,72 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity import com.faraphel.tasks_valider.database.entities.SessionEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class RelationPersonSessionSubjectApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) +class RelationPersonSessionSubjectApi( + private val dao: BaseTaskDao, + private val session: SessionEntity +) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, RelationPersonSessionSubjectEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, RelationPersonSessionSubjectEntity::class.java) + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, RelationPersonSessionSubjectEntity::class.java) + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SessionApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SessionApi.kt index eafbc25..4c65413 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SessionApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SessionApi.kt @@ -1,13 +1,71 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.SessionEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class SessionApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) + +class SessionApi( + private val dao: BaseTaskDao, + private val session: SessionEntity +) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, SessionEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, SessionEntity::class.java) + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, SessionEntity::class.java) + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SubjectApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SubjectApi.kt index fbbed53..252cd7f 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SubjectApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/SubjectApi.kt @@ -1,15 +1,72 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.SubjectEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class SubjectApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) +class SubjectApi( + private val dao: BaseTaskDao, + private val session: SessionEntity +) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, SubjectEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, SubjectEntity::class.java) + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, SubjectEntity::class.java) + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/TaskApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/TaskApi.kt index 6f5351e..2292dfd 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/TaskApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/TaskApi.kt @@ -1,15 +1,72 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.TaskEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class TaskApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) +class TaskApi( + private val dao: BaseTaskDao, + private val session: SessionEntity +) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, TaskEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, TaskEntity::class.java) + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, TaskEntity::class.java) + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ValidationApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ValidationApi.kt index 46132b6..266e1d0 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ValidationApi.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/ValidationApi.kt @@ -1,15 +1,74 @@ package com.faraphel.tasks_valider.database.api.entities -import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi +import com.faraphel.tasks_valider.database.api.entities.base.BaseApi import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao import com.faraphel.tasks_valider.database.entities.SessionEntity import com.faraphel.tasks_valider.database.entities.ValidationEntity -import com.google.gson.reflect.TypeToken +import com.faraphel.tasks_valider.utils.getBody +import com.google.gson.Gson +import fi.iki.elonen.NanoHTTPD -class ValidationApi(dao: BaseTaskDao, session: SessionEntity) : - BaseTaskApi( - dao, - object: TypeToken() {}, - session - ) +class ValidationApi( + private val dao: BaseTaskDao, + private val session: SessionEntity +) : BaseApi { + companion object { + private val parser = Gson() ///< The JSON parser + } + + // Requests + + override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, ValidationEntity::class.java) + // check if the object is in the object accessible from the session + val exists = this.dao.getAllBySession(session.id).contains(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + if (exists) "Exists" else "Not found" + ) + } + + override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.OK, + "application/json", + parser.toJson(this.dao.getAllBySession(session.id)) + ) + } + + override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, ValidationEntity::class.java) + // save the data into the database + val id = this.dao.insert(obj) + + return NanoHTTPD.newFixedLengthResponse( + NanoHTTPD.Response.Status.CREATED, + "text/plain", + id.toString() + ) + } + + override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { + // get the content of the request + val data = httpSession.getBody() + // parse the object + val obj = parser.fromJson(data, ValidationEntity::class.java) + // delete the object from the database + val count = this.dao.delete(obj) + + return NanoHTTPD.newFixedLengthResponse( + if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, + "text/plain", + count.toString() + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/base/BaseJsonApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/base/BaseJsonApi.kt deleted file mode 100644 index 71441f7..0000000 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/base/BaseJsonApi.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.faraphel.tasks_valider.database.api.entities.base - -import android.util.Log -import com.faraphel.tasks_valider.database.dao.base.BaseCronDao -import com.faraphel.tasks_valider.database.entities.base.BaseEntity -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import fi.iki.elonen.NanoHTTPD - -/** - * A base for the API to handle the database operations. - * This is preconfigured to handle JSON data. - * @param Entity the entity type to handle - */ -abstract class BaseJsonApi( - private val dao: BaseCronDao, - private val entityTypeToken: TypeToken, -) : BaseApi { - companion object { - private val parser = Gson() ///< The JSON parser - } - - // Requests - - override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { - val obj = parser.fromJson( - httpSession.inputStream.bufferedReader().readText(), - this.entityTypeToken.type - ) - val exists = this.dao.exists(obj) - - return NanoHTTPD.newFixedLengthResponse( - if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, - "text/plain", - if (exists) "Exists" else "Not found" - ) - } - - override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { - return NanoHTTPD.newFixedLengthResponse( - NanoHTTPD.Response.Status.OK, - "application/json", - parser.toJson(this.dao.getAll()) - ) - } - - override fun post(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { - val data = httpSession.inputStream.bufferedReader().readText() - Log.i("post data", "data raw : $data") - - val obj = parser.fromJson( - data, - this.entityTypeToken.type - ) - - Log.i("post data", "data parsed : $obj") - - val id = this.dao.insert(obj) - - return NanoHTTPD.newFixedLengthResponse( - NanoHTTPD.Response.Status.CREATED, - "text/plain", - id.toString() - ) - } - - override fun delete(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { - val obj = parser.fromJson( - httpSession.inputStream.bufferedReader().readText(), - this.entityTypeToken.type - ) - val count = this.dao.delete(obj) - - return NanoHTTPD.newFixedLengthResponse( - if (count > 0) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, - "text/plain", - count.toString() - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/base/BaseTaskApi.kt b/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/base/BaseTaskApi.kt deleted file mode 100644 index b22ca9f..0000000 --- a/app/src/main/java/com/faraphel/tasks_valider/database/api/entities/base/BaseTaskApi.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.faraphel.tasks_valider.database.api.entities.base - -import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao -import com.faraphel.tasks_valider.database.entities.SessionEntity -import com.faraphel.tasks_valider.database.entities.base.BaseEntity -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import fi.iki.elonen.NanoHTTPD - -/** - * A base for the API to handle the database operations. - * This is preconfigured to handle data for Task objects. - * @param Entity the entity type to handle - */ -abstract class BaseTaskApi( - private val dao: BaseTaskDao, - private val entityTypeToken: TypeToken, - private val session: SessionEntity, -) : BaseJsonApi( - dao, - entityTypeToken, -) { - companion object { - private val parser = Gson() ///< The JSON parser - } - - // Requests - - override fun head(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { - val obj = parser.fromJson( - httpSession.inputStream.bufferedReader().readText(), - this.entityTypeToken.type - ) - // check if the object is in the object accessible from the session - val exists = this.dao.getAllBySession(session.id).contains(obj) - - return NanoHTTPD.newFixedLengthResponse( - if (exists) NanoHTTPD.Response.Status.OK else NanoHTTPD.Response.Status.NOT_FOUND, - "text/plain", - if (exists) "Exists" else "Not found" - ) - } - - override fun get(httpSession: NanoHTTPD.IHTTPSession): NanoHTTPD.Response { - return NanoHTTPD.newFixedLengthResponse( - NanoHTTPD.Response.Status.OK, - "application/json", - parser.toJson(this.dao.getAllBySession(session.id)) - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/authentification/server.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/authentification/server.kt index 0ac5504..f9d5084 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/authentification/server.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/authentification/server.kt @@ -1,6 +1,6 @@ package com.faraphel.tasks_valider.ui.screen.authentification -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.* import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.material3.TextField @@ -8,6 +8,10 @@ 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.unit.dp +import androidx.compose.ui.unit.sp import com.faraphel.tasks_valider.connectivity.task.session.TaskRole import com.faraphel.tasks_valider.database.entities.PersonEntity @@ -20,19 +24,37 @@ fun AuthentificationServerScreen(personEntity: MutableState) { val firstName = remember { mutableStateOf("") } val lastName = remember { mutableStateOf("") } - Column { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + // title + Text( + text = "Your Profile", + fontSize = 32.sp + ) + + // separator + Spacer(modifier = Modifier.height(24.dp)) + // first name TextField( value = firstName.value, + placeholder = { Text("first name") }, onValueChange = { text -> firstName.value = text }, ) // last name TextField( value = lastName.value, + placeholder = { Text("last name") }, onValueChange = { text -> lastName.value = text }, ) + // separator + Spacer(modifier = Modifier.height(24.dp)) + // confirm button Button(onClick = { // create the person entity with the given information diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/selection.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/selection.kt index 0f9424c..1575f12 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/selection.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/selection.kt @@ -3,10 +3,14 @@ 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.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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 @@ -29,7 +33,20 @@ fun CommunicationInternetSelectScreen(activity: Activity, database: TaskDatabase @Composable fun CommunicationInternetSelectContent(controller: NavController) { - Column { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + // title + Text( + text = "Role", + fontSize = 32.sp + ) + + // separator + Spacer(modifier = Modifier.height(24.dp)) + // client mode Button(onClick = { controller.navigate("client") }) { Text("Client") } // server mode diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server.kt index 626fae9..2f81efa 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server.kt @@ -4,8 +4,7 @@ import android.app.Activity import android.os.Build import android.util.Log import androidx.annotation.RequiresApi -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenu @@ -13,7 +12,14 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextMeasurer import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController @@ -93,43 +99,71 @@ fun CommunicationInternetServerContent( Thread { refreshClasses(database, classes) }.start() } - Column { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + // title + Text( + text = "New Session", + fontSize = 32.sp + ) + + // separator + Spacer(modifier = Modifier.height(24.dp)) + // classes - Button(onClick = { areClassesExpanded.value = !areClassesExpanded.value }) { - Row { - Text(text = "Class") + Row(verticalAlignment = Alignment.CenterVertically) { + // description + Text(text = "Class", fontSize = 12.sp) + // separator + Spacer(modifier = Modifier.width(width = 12.dp)) + // selector + Button(onClick = { areClassesExpanded.value = !areClassesExpanded.value }) { // display the selected class, if selected - if (selectedClass.value != null) - Text(text = selectedClass.value!!.name) + if (selectedClass.value != null) Text(text = selectedClass.value!!.name) + else Text(text = "") } - } - DropdownMenu( - expanded = areClassesExpanded.value, - onDismissRequest = { areClassesExpanded.value = false } - ) { - // TODO(Faraphel): student lists should be loaded from the database or a file - classes.value?.forEach { class_ -> - DropdownMenuItem( - text = { Text(class_.name) }, - onClick = { - selectedClass.value = class_ - areClassesExpanded.value = false - } - ) + + // class selector + DropdownMenu( + expanded = areClassesExpanded.value, + onDismissRequest = { areClassesExpanded.value = false } + ) { + // TODO(Faraphel): student lists should be loaded from the database or a file + classes.value?.forEach { class_ -> + DropdownMenuItem( + text = { Text(class_.name) }, + onClick = { + selectedClass.value = class_ + areClassesExpanded.value = false + } + ) + } } } // server port - TextField( - value = serverPort.intValue.toString(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - onValueChange = { text -> - val port = text.toInt() - if (port in RANGE_SERVER_PORT) { - serverPort.intValue = port + Row(verticalAlignment = Alignment.CenterVertically) { + // descriptor + Text(text = "Port") + // separator + Spacer(modifier = Modifier.width(width = 12.dp)) + // input + TextField( + modifier = Modifier.width(80.dp), + value = serverPort.intValue.toString(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true, + onValueChange = { text -> + val port = text.toInt() + if (port in RANGE_SERVER_PORT) { + serverPort.intValue = port + } } - } - ) + ) + } // check if a class is selected if (selectedClass.value != null) diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/selection.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/selection.kt index d95ea0b..646f783 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/selection.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/selection.kt @@ -4,12 +4,17 @@ import android.app.Activity import android.os.Build import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.* import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +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 @@ -58,7 +63,20 @@ fun CommunicationModeSelectionScreen(activity: Activity, database: TaskDatabase) fun CommunicationSelectContent(controller: NavController, activity: Activity) { val isWifiP2pSupported = BwfManager.isSupported(activity) - Column { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + // title + Text( + text = "Connection Type", + fontSize = 32.sp + ) + + // separator + Spacer(modifier = Modifier.height(24.dp)) + // internet communication mode Button(onClick = { controller.navigate("internet") }) { Text("Internet") @@ -68,7 +86,7 @@ fun CommunicationSelectContent(controller: NavController, activity: Activity) { Button( colors = ButtonDefaults.buttonColors( // if the WiFi-Direct is not supported, the button is grayed out - containerColor = if (isWifiP2pSupported) Color.Unspecified else Color.Gray + containerColor = if (isWifiP2pSupported) MaterialTheme.colorScheme.primary else Color.Gray ), onClick = { // if the WiFi-Direct is supported, navigate to the WiFi-Direct screen diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/session.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/session.kt index d9dea02..3cbc7db 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/session.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/session.kt @@ -4,13 +4,17 @@ import android.app.Activity import android.os.Build import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.* import androidx.compose.material3.Button import androidx.compose.material3.Text 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.unit.dp +import androidx.compose.ui.unit.sp import com.faraphel.tasks_valider.connectivity.task.TaskClient import com.faraphel.tasks_valider.database.entities.PersonEntity import com.google.gson.Gson @@ -44,10 +48,25 @@ fun TaskSessionScreen( selectedStudent.value!! ) - Column { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // title + Text( + text = "Session", + fontSize = 32.sp + ) + + // separator + Spacer(modifier = Modifier.height(24.dp)) + // if the groups have already been defined, display them for (student in students.value!!) { - Button(onClick = { selectedStudent.value = student }) { + Button( + modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp, horizontal = 16.dp), + onClick = { selectedStudent.value = student }, + ) { Text(text = student.fullName()) } } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/student.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/student.kt index e5c0005..57ac420 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/student.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/student.kt @@ -5,8 +5,7 @@ import android.os.Build import android.util.Log import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.* import androidx.compose.material3.Button import androidx.compose.material3.Checkbox import androidx.compose.material3.Text @@ -14,6 +13,11 @@ 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.font.FontWeight +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.TaskPermission import com.faraphel.tasks_valider.database.entities.* @@ -25,7 +29,6 @@ import java.time.Instant * This screen represent a student * @param student the student object */ -@RequiresApi(Build.VERSION_CODES.O) @Composable fun TaskStudentScreen( activity: Activity, @@ -47,8 +50,17 @@ fun TaskStudentScreen( ) }.start() - Column { - Text(text = student.fullName()) + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // title + Text(text = "Student", fontSize = 32.sp) + // student name - subtitle + Text(text = student.fullName(), fontSize = 24.sp) + + // separator + Spacer(modifier = Modifier.height(24.dp)) // if both the list of tasks and validations are loaded if (!(tasks.value == null || validations.value == null)) { @@ -56,22 +68,31 @@ fun TaskStudentScreen( // get the validation val validation = validations.value!!.firstOrNull { validation -> validation.taskId == task.id } - Button(onClick = {}) { + Box( + modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp, horizontal = 64.dp), + ) { Row { Column { // task title - Text(task.title) + Text(text = task.title, fontWeight = FontWeight.Bold) // task description task.description?.let { description -> Text(description) } // if the task have been validated, show the date if (validation != null) Text(validation.date.toString()) } + + // separator + Spacer(modifier = Modifier.fillMaxWidth()) + // the validation state Checkbox( checked = validation != null, enabled = user.role.permissions.contains(TaskPermission.WRITE), onCheckedChange = { state -> Thread { + // TODO(Faraphel): simplify or put the UI refresh in the update function ? + + // send a notification to the server about the validation updateValidation( client, state, @@ -82,6 +103,14 @@ fun TaskStudentScreen( task.id, ) ) + // refresh the UI + refreshTasksValidations( + activity, + client, + student, + tasks, + validations + ) }.start() } ) diff --git a/app/src/main/java/com/faraphel/tasks_valider/utils/requests.kt b/app/src/main/java/com/faraphel/tasks_valider/utils/requests.kt new file mode 100644 index 0000000..71f6fe9 --- /dev/null +++ b/app/src/main/java/com/faraphel/tasks_valider/utils/requests.kt @@ -0,0 +1,22 @@ +package com.faraphel.tasks_valider.utils + +import fi.iki.elonen.NanoHTTPD +import java.nio.charset.Charset + + +/** + * Return the body of a request as a string. + * :param charset: the encoding of the body + */ +fun NanoHTTPD.IHTTPSession.getBody( + charset: Charset = Charset.forName("UTF-8") +): String { + // get the length of the body + val length = this.headers["content-length"]!!.toInt() + // prepare a buffer for the body + val buffer = ByteArray(length) + // read the body into the buffer + this.inputStream.read(buffer, 0, length) + // convert that buffer into a string + return buffer.toString(charset) +}