implemented a better API for the client and the quick validation system
This commit is contained in:
parent
48a3932f64
commit
e12339abb5
58 changed files with 675 additions and 323 deletions
|
@ -7,14 +7,14 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.activity.compose.setContent
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.room.Room
|
||||
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager
|
||||
import com.faraphel.tasks_valider.connectivity.bwd.BwdManager
|
||||
import com.faraphel.tasks_valider.database.TaskDatabase
|
||||
import com.faraphel.tasks_valider.database.populateTaskDatabaseTest
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.CommunicationModeSelectionScreen
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private var bwfManager: BwfManager? = null ///< the WiFi-Direct helper
|
||||
private var bwdManager: BwdManager? = null ///< the WiFi-Direct helper
|
||||
private lateinit var database: TaskDatabase ///< the database manager
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
|
@ -50,13 +50,13 @@ class MainActivity : ComponentActivity() {
|
|||
super.onResume()
|
||||
|
||||
// enable the WiFi-Direct events
|
||||
this.registerReceiver(this.bwfManager, BwfManager.ALL_INTENT_FILTER)
|
||||
this.registerReceiver(this.bwdManager, BwdManager.ALL_INTENT_FILTER)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
// disable the WiFi-Direct events
|
||||
this.unregisterReceiver(this.bwfManager)
|
||||
this.unregisterReceiver(this.bwdManager)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf
|
||||
package com.faraphel.tasks_valider.connectivity.bwd
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
|
@ -10,7 +10,7 @@ import android.content.pm.PackageManager
|
|||
import android.net.wifi.p2p.*
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.faraphel.tasks_valider.connectivity.bwf.error.*
|
||||
import com.faraphel.tasks_valider.connectivity.bwd.error.*
|
||||
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,7 @@ import com.faraphel.tasks_valider.connectivity.bwf.error.*
|
|||
* @param manager The WiFi-Direct manager
|
||||
* @param channel The WiFi-Direct channel
|
||||
*/
|
||||
class BwfManager(
|
||||
class BwdManager(
|
||||
private var manager: WifiP2pManager,
|
||||
private var channel: WifiP2pManager.Channel,
|
||||
) : BroadcastReceiver() {
|
||||
|
@ -41,11 +41,11 @@ class BwfManager(
|
|||
* Create a new BwfManager from an activity.
|
||||
* @param activity The activity to create the manager from
|
||||
*/
|
||||
fun fromActivity(activity: Activity): BwfManager {
|
||||
fun fromActivity(activity: Activity): BwdManager {
|
||||
// check if the system support WiFi-Direct
|
||||
if (this.isSupported(activity)) {
|
||||
Log.e("wifi-p2p", "this device does not support the WiFi-Direct feature")
|
||||
throw BwfNotSupportedException()
|
||||
throw BwdNotSupportedException()
|
||||
}
|
||||
|
||||
// TODO(Faraphel): more check on permissions
|
||||
|
@ -62,11 +62,11 @@ class BwfManager(
|
|||
|
||||
// get the WiFi-Direct manager
|
||||
val manager = activity.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
|
||||
?: throw BwfPermissionException()
|
||||
?: throw BwdPermissionException()
|
||||
|
||||
// get the WiFi-Direct channel
|
||||
val channel = manager.initialize(activity, activity.mainLooper, null)
|
||||
return BwfManager(manager, channel)
|
||||
return BwdManager(manager, channel)
|
||||
|
||||
// NOTE(Faraphel): the broadcast receiver should be registered in the activity onResume
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ class BwfManager(
|
|||
fun connect(config: WifiP2pConfig, callback: () -> Unit = {}) =
|
||||
this.manager.connect(this.channel, config, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfConnectException(reason)
|
||||
override fun onFailure(reason: Int) = throw BwdConnectException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
|
@ -105,7 +105,7 @@ class BwfManager(
|
|||
fun discoverPeers(callback: () -> Unit = {}) =
|
||||
this.manager.discoverPeers(this.channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfDiscoverException(reason)
|
||||
override fun onFailure(reason: Int) = throw BwdDiscoverException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
|
@ -133,7 +133,7 @@ class BwfManager(
|
|||
fun createGroup(callback: () -> Unit = {}) =
|
||||
this.manager.createGroup(this.channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfCreateGroupException(reason)
|
||||
override fun onFailure(reason: Int) = throw BwdCreateGroupException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
|
@ -143,7 +143,7 @@ class BwfManager(
|
|||
fun removeGroup(callback: () -> Unit = {}) =
|
||||
this.manager.removeGroup(this.channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfRemoveGroupException(reason)
|
||||
override fun onFailure(reason: Int) = throw BwdRemoveGroupException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
|
@ -161,7 +161,7 @@ class BwfManager(
|
|||
this.requestGroupInfo { group ->
|
||||
// if a group exist, quit it
|
||||
if (group != null)
|
||||
this.removeGroup { this@BwfManager.createGroup(callback) }
|
||||
this.removeGroup { this@BwdManager.createGroup(callback) }
|
||||
else
|
||||
// create the group
|
||||
this.createGroup(callback)
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
class BwdConnectException(
|
||||
reason: Int
|
||||
) : BwdException("Cannot connect to the peer. Reason: $reason")
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
class BwdCreateGroupException (
|
||||
reason: Int
|
||||
) : BwdException("Could not create the group : $reason")
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
class BwdDiscoverException(
|
||||
reason: Int
|
||||
) : BwdException("Could not discover peers : $reason")
|
|
@ -1,9 +1,9 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
|
||||
/**
|
||||
* Base Exception for everything concerning the WifiP2pHelper class
|
||||
*/
|
||||
open class BwfException(
|
||||
open class BwdException(
|
||||
override val message: String?
|
||||
) : Exception(message)
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
class BwdInvalidActionException(
|
||||
action: String
|
||||
) : BwdException("This WiFi-Direct action is not supported : $action")
|
|
@ -0,0 +1,4 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
class BwdNotSupportedException :
|
||||
BwdException("WiFi-Direct is not supported on this device.")
|
|
@ -0,0 +1,4 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
class BwdPermissionException :
|
||||
BwdException("WiFi-Direct requires permissions to work properly. Please grant the permissions.")
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwd.error
|
||||
|
||||
class BwdRemoveGroupException (
|
||||
reason: Int
|
||||
) : BwdException("Could not remove the group : $reason")
|
|
@ -1,5 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfConnectException(
|
||||
reason: Int
|
||||
) : BwfException("Cannot connect to the peer. Reason: $reason")
|
|
@ -1,5 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfCreateGroupException (
|
||||
reason: Int
|
||||
) : BwfException("Could not create the group : $reason")
|
|
@ -1,5 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfDiscoverException(
|
||||
reason: Int
|
||||
) : BwfException("Could not discover peers : $reason")
|
|
@ -1,5 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfInvalidActionException(
|
||||
action: String
|
||||
) : BwfException("This WiFi-Direct action is not supported : $action")
|
|
@ -1,4 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfNotSupportedException :
|
||||
BwfException("WiFi-Direct is not supported on this device.")
|
|
@ -1,4 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfPermissionException :
|
||||
BwfException("WiFi-Direct requires permissions to work properly. Please grant the permissions.")
|
|
@ -1,5 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfRemoveGroupException (
|
||||
reason: Int
|
||||
) : BwfException("Could not remove the group : $reason")
|
|
@ -1,16 +1,11 @@
|
|||
package com.faraphel.tasks_valider.connectivity.task
|
||||
|
||||
import okhttp3.HttpUrl
|
||||
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
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.*
|
||||
|
||||
|
||||
/**
|
||||
* A client to handle the room connection.
|
||||
* A client to handle the room connection and access the API
|
||||
* @param address the address of the server
|
||||
* @param port the port of the server
|
||||
* @param baseCookies list of cookies to use (optional)
|
||||
|
@ -20,86 +15,15 @@ class TaskClient(
|
|||
private val port: Int,
|
||||
private val baseCookies: List<okhttp3.Cookie> = listOf()
|
||||
) {
|
||||
private val baseUrl = "http://$address:$port"
|
||||
private val client = OkHttpClient().newBuilder()
|
||||
.cookieJar(
|
||||
// TODO(Faraphel): should be moved into another object
|
||||
object : okhttp3.CookieJar {
|
||||
private val cookies = baseCookies.toMutableList() ///< list of cookies
|
||||
private val httpClient = TaskEntityHttpClient(address, port, baseCookies)
|
||||
|
||||
override fun loadForRequest(url: HttpUrl): List<okhttp3.Cookie> {
|
||||
return this.cookies
|
||||
}
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<okhttp3.Cookie>) {
|
||||
this.cookies.addAll(cookies)
|
||||
}
|
||||
}
|
||||
).callTimeout(30.seconds)
|
||||
.build()
|
||||
val clientApi = ClassClientApi(httpClient)
|
||||
val personApi = PersonClientApi(httpClient)
|
||||
val sessionApi = SessionClientApi(httpClient)
|
||||
val subjectApi = SubjectClientApi(httpClient)
|
||||
val taskApi = TaskClientApi(httpClient)
|
||||
val validationApi = ValidationClientApi(httpClient)
|
||||
|
||||
// TODO(Faraphel): automatically convert content to the correct type ?
|
||||
|
||||
/**
|
||||
* Return a basic request to the server
|
||||
* @param endpoint the endpoint of the server
|
||||
*/
|
||||
private fun baseRequestBuilder(endpoint: String): okhttp3.Request.Builder =
|
||||
okhttp3.Request.Builder().url("$baseUrl/$endpoint")
|
||||
|
||||
/**
|
||||
* Run a HEAD request
|
||||
* @param endpoint the endpoint of the server
|
||||
*/
|
||||
fun head(endpoint: String): okhttp3.Request =
|
||||
this.baseRequestBuilder(endpoint).head().build()
|
||||
|
||||
/**
|
||||
* Run a GET request
|
||||
* @param endpoint the endpoint of the server
|
||||
*/
|
||||
fun get(endpoint: String): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.get()
|
||||
.build()
|
||||
).execute()
|
||||
|
||||
/**
|
||||
* Run a POST request
|
||||
* @param endpoint the endpoint of the server
|
||||
* @param content the content of the request
|
||||
* @param type the type of the content
|
||||
*/
|
||||
fun post(endpoint: String, content: String, type: String = "text/plain"): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.post(content.toRequestBody(type.toMediaType()))
|
||||
.build()
|
||||
).execute()
|
||||
|
||||
/**
|
||||
* Run a PATCH request
|
||||
* @param endpoint the endpoint of the server
|
||||
* @param content the content of the request
|
||||
* @param type the type of the content
|
||||
*/
|
||||
fun patch(endpoint: String, content: String, type: String = "text/plain"): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.patch(content.toRequestBody(type.toMediaType()))
|
||||
.build()
|
||||
).execute()
|
||||
|
||||
/**
|
||||
* Run a DELETE request
|
||||
* @param endpoint the endpoint of the server
|
||||
* @param content the content of the request
|
||||
* @param type the type of the content
|
||||
*/
|
||||
fun delete(endpoint: String, content: String, type: String = "text/plain"): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.delete(content.toRequestBody(type.toMediaType()))
|
||||
.build()
|
||||
).execute()
|
||||
val relationClassPersonApi = RelationClassPersonClientApi(httpClient)
|
||||
val relationPersonSessionSubjectApi = RelationPersonSessionSubjectClientApi(httpClient)
|
||||
}
|
|
@ -3,7 +3,7 @@ package com.faraphel.tasks_valider.connectivity.task
|
|||
import com.faraphel.tasks_valider.connectivity.task.api.TaskSessionManagerApi
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSessionManager
|
||||
import com.faraphel.tasks_valider.database.TaskDatabase
|
||||
import com.faraphel.tasks_valider.database.api.TaskDatabaseApi
|
||||
import com.faraphel.tasks_valider.database.api.server.DatabaseApi
|
||||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
import com.faraphel.tasks_valider.database.entities.SessionEntity
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
|
@ -23,7 +23,7 @@ class TaskServer(
|
|||
private val adminPersonEntity: PersonEntity,
|
||||
) : NanoHTTPD(port) {
|
||||
private val sessionManager = TaskSessionManager(adminPersonEntity) ///< the session manager
|
||||
private val databaseApi = TaskDatabaseApi(this.database, session) ///< the api of the database
|
||||
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
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.faraphel.tasks_valider.database
|
|||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import com.faraphel.tasks_valider.database.converters.InstantConverter
|
||||
import com.faraphel.tasks_valider.utils.converters.InstantConverter
|
||||
import com.faraphel.tasks_valider.database.dao.*
|
||||
import com.faraphel.tasks_valider.database.entities.*
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package com.faraphel.tasks_valider.database.api.client
|
||||
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
||||
/**
|
||||
* An HTTP client to handle the room connection.
|
||||
* @param address the address of the server
|
||||
* @param port the port of the server
|
||||
* @param baseCookies list of cookies to use (optional)
|
||||
*/
|
||||
class TaskEntityHttpClient(
|
||||
private val address: String,
|
||||
private val port: Int,
|
||||
private val baseCookies: List<okhttp3.Cookie> = listOf()
|
||||
) {
|
||||
// the base url for the server
|
||||
private val baseUrl = "http://$address:$port"
|
||||
// the HTTP client
|
||||
private val client = OkHttpClient().newBuilder()
|
||||
.cookieJar(
|
||||
object : okhttp3.CookieJar {
|
||||
private val cookies = baseCookies.toMutableList() ///< list of cookies
|
||||
override fun loadForRequest(url: HttpUrl) = this.cookies
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<okhttp3.Cookie>) { this.cookies.addAll(cookies) }
|
||||
}
|
||||
)
|
||||
.callTimeout(30.seconds)
|
||||
.build()
|
||||
|
||||
/**
|
||||
* Return a basic request to the server
|
||||
* @param endpoint the endpoint of the server
|
||||
*/
|
||||
private fun baseRequestBuilder(endpoint: String): okhttp3.Request.Builder =
|
||||
okhttp3.Request.Builder().url("$baseUrl/$endpoint")
|
||||
|
||||
/**
|
||||
* Run a HEAD request
|
||||
* @param endpoint the endpoint of the server
|
||||
*/
|
||||
fun head(endpoint: String): okhttp3.Request =
|
||||
this.baseRequestBuilder(endpoint).head().build()
|
||||
|
||||
/**
|
||||
* Run a GET request
|
||||
* @param endpoint the endpoint of the server
|
||||
*/
|
||||
fun get(endpoint: String): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.get()
|
||||
.build()
|
||||
).execute()
|
||||
|
||||
/**
|
||||
* Run a POST request
|
||||
* @param endpoint the endpoint of the server
|
||||
* @param content the content of the request
|
||||
* @param type the type of the content
|
||||
*/
|
||||
fun post(endpoint: String, content: String, type: String = "text/plain"): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.post(content.toRequestBody(type.toMediaType()))
|
||||
.build()
|
||||
).execute()
|
||||
|
||||
/**
|
||||
* Run a PATCH request
|
||||
* @param endpoint the endpoint of the server
|
||||
* @param content the content of the request
|
||||
* @param type the type of the content
|
||||
*/
|
||||
fun patch(endpoint: String, content: String, type: String = "text/plain"): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.patch(content.toRequestBody(type.toMediaType()))
|
||||
.build()
|
||||
).execute()
|
||||
|
||||
/**
|
||||
* Run a DELETE request
|
||||
* @param endpoint the endpoint of the server
|
||||
* @param content the content of the request
|
||||
* @param type the type of the content
|
||||
*/
|
||||
fun delete(endpoint: String, content: String, type: String = "text/plain"): okhttp3.Response =
|
||||
this.client.newCall(
|
||||
this.baseRequestBuilder(endpoint)
|
||||
.delete(content.toRequestBody(type.toMediaType()))
|
||||
.build()
|
||||
).execute()
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.ClassEntity
|
||||
|
||||
|
||||
class ClassClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<ClassEntity>(
|
||||
client,
|
||||
ClassEntity::class
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
|
||||
|
||||
class PersonClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<PersonEntity>(
|
||||
client,
|
||||
PersonEntity::class
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.RelationClassPersonEntity
|
||||
|
||||
|
||||
class RelationClassPersonClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<RelationClassPersonEntity>(
|
||||
client,
|
||||
RelationClassPersonEntity::class
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.RelationPersonSessionSubjectEntity
|
||||
|
||||
|
||||
class RelationPersonSessionSubjectClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<RelationPersonSessionSubjectEntity>(
|
||||
client,
|
||||
RelationPersonSessionSubjectEntity::class
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.SessionEntity
|
||||
|
||||
|
||||
class SessionClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<SessionEntity>(
|
||||
client,
|
||||
SessionEntity::class
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.SubjectEntity
|
||||
|
||||
|
||||
class SubjectClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<SubjectEntity>(
|
||||
client,
|
||||
SubjectEntity::class
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.TaskEntity
|
||||
|
||||
|
||||
class TaskClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<TaskEntity>(
|
||||
client,
|
||||
TaskEntity::class
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.api.client.entities.base.BaseClientApi
|
||||
import com.faraphel.tasks_valider.database.entities.ValidationEntity
|
||||
|
||||
|
||||
class ValidationClientApi(
|
||||
client: TaskEntityHttpClient,
|
||||
) : BaseClientApi<ValidationEntity>(
|
||||
client,
|
||||
ValidationEntity::class
|
||||
)
|
|
@ -0,0 +1,102 @@
|
|||
package com.faraphel.tasks_valider.database.api.client.entities.base;
|
||||
|
||||
import android.util.Log
|
||||
import com.faraphel.tasks_valider.database.api.client.TaskEntityHttpClient
|
||||
import com.faraphel.tasks_valider.database.entities.base.BaseEntity
|
||||
import com.faraphel.tasks_valider.database.entities.error.HttpException
|
||||
import com.faraphel.tasks_valider.utils.parser
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.companionObject
|
||||
import kotlin.reflect.full.companionObjectInstance
|
||||
import kotlin.reflect.full.declaredMemberProperties
|
||||
|
||||
|
||||
abstract class BaseClientApi<Entity: BaseEntity>(
|
||||
private val client: TaskEntityHttpClient,
|
||||
private val entityType: KClass<Entity>,
|
||||
) {
|
||||
/**
|
||||
* return the API endpoint for this entity
|
||||
* @return the API endpoint for this entity
|
||||
*/
|
||||
private fun getEndpoint(): String {
|
||||
// get the property for the name of the table
|
||||
val propertyTableName = entityType.companionObject!!.declaredMemberProperties.first { member ->
|
||||
member.name == "TABLE_NAME"
|
||||
}
|
||||
// get the table name by calling the getter of the property
|
||||
val tableName = propertyTableName.getter.call(entityType.companionObjectInstance)
|
||||
// return the endpoint
|
||||
return "entities/${tableName}"
|
||||
}
|
||||
|
||||
/**
|
||||
* return all the entities for that table
|
||||
* @return all the entities for that table
|
||||
* @throws java.io.IOException reading error while parsing request
|
||||
* @throws HttpException error of the request
|
||||
*/
|
||||
fun getAll(): List<Entity> {
|
||||
// try to obtain the list of validations
|
||||
Log.i("base-api", this.getEndpoint())
|
||||
val response = client.get(this.getEndpoint())
|
||||
|
||||
// in case of error, notify it
|
||||
if (!response.isSuccessful)
|
||||
throw HttpException(response.code)
|
||||
|
||||
val data = response.body.string()
|
||||
Log.i("base-api", data)
|
||||
|
||||
// parse the list of validations
|
||||
return parser.fromJson(
|
||||
data,
|
||||
TypeToken.getParameterized(ArrayList::class.java, entityType.java).type
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new entity in the table
|
||||
* @return the id of the object in the database
|
||||
* @throws java.io.IOException reading error while parsing request
|
||||
* @throws HttpException error of the request
|
||||
*/
|
||||
fun save(entity: Entity): Long {
|
||||
// try to send the serialized entity as json
|
||||
val response = client.post(
|
||||
this.getEndpoint(),
|
||||
parser.toJson(entity),
|
||||
"application/json; charset=utf-8"
|
||||
)
|
||||
|
||||
// in case of error, notify it
|
||||
if (!response.isSuccessful)
|
||||
throw HttpException(response.code)
|
||||
|
||||
// return the id of the object
|
||||
return response.body.string().toLong()
|
||||
}
|
||||
|
||||
/**
|
||||
* delete an entity in the table
|
||||
* @return the number of object deleted in the database
|
||||
* @throws java.io.IOException reading error while parsing request
|
||||
* @throws HttpException error of the request
|
||||
*/
|
||||
fun delete(entity: Entity): Long {
|
||||
// try to delete the object
|
||||
val response = client.delete(
|
||||
this.getEndpoint(),
|
||||
parser.toJson(entity),
|
||||
"application/json; charset=utf-8"
|
||||
)
|
||||
|
||||
// in case of error, notify it
|
||||
if (!response.isSuccessful)
|
||||
throw HttpException(response.code)
|
||||
|
||||
// return the id of the object
|
||||
return response.body.string().toLong()
|
||||
}
|
||||
}
|
|
@ -1,27 +1,28 @@
|
|||
package com.faraphel.tasks_valider.database.api
|
||||
package com.faraphel.tasks_valider.database.api.server
|
||||
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskPermission
|
||||
import com.faraphel.tasks_valider.connectivity.task.session.TaskSession
|
||||
import com.faraphel.tasks_valider.database.TaskDatabase
|
||||
import com.faraphel.tasks_valider.database.api.entities.*
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseDatabaseApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.*
|
||||
import com.faraphel.tasks_valider.database.entities.*
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
|
||||
class TaskDatabaseApi(
|
||||
|
||||
class DatabaseApi(
|
||||
private val database: TaskDatabase,
|
||||
private val session: SessionEntity,
|
||||
) {
|
||||
private val api: Map<String, BaseApi> = mapOf(
|
||||
ClassEntity.TABLE_NAME to ClassApi(this.database.classDao(), session),
|
||||
PersonEntity.TABLE_NAME to PersonApi(this.database.personDao(), session),
|
||||
SessionEntity.TABLE_NAME to SessionApi(this.database.sessionDao(), session),
|
||||
SubjectEntity.TABLE_NAME to SubjectApi(this.database.subjectDao(), session),
|
||||
TaskEntity.TABLE_NAME to TaskApi(this.database.taskDao(), session),
|
||||
ValidationEntity.TABLE_NAME to ValidationApi(this.database.validationDao(), session),
|
||||
private val api: Map<String, BaseDatabaseApi> = mapOf(
|
||||
ClassEntity.TABLE_NAME to ClassDatabaseApi(this.database.classDao(), session),
|
||||
PersonEntity.TABLE_NAME to PersonDatabaseApi(this.database.personDao(), session),
|
||||
SessionEntity.TABLE_NAME to SessionDatabaseApi(this.database.sessionDao(), session),
|
||||
SubjectEntity.TABLE_NAME to SubjectDatabaseApi(this.database.subjectDao(), session),
|
||||
TaskEntity.TABLE_NAME to TaskDatabaseApi(database.taskDao(), session),
|
||||
ValidationEntity.TABLE_NAME to ValidationDatabaseApi(this.database.validationDao(), session),
|
||||
|
||||
RelationClassPersonEntity.TABLE_NAME to RelationClassPersonApi(this.database.relationClassPersonDao(), session),
|
||||
RelationPersonSessionSubjectEntity.TABLE_NAME to RelationPersonSessionSubjectApi(this.database.relationPersonSessionSubjectDao(), session),
|
||||
RelationClassPersonEntity.TABLE_NAME to RelationClassPersonDatabaseApi(this.database.relationClassPersonDao(), session),
|
||||
RelationPersonSessionSubjectEntity.TABLE_NAME to RelationPersonSessionSubjectDatabaseApi(this.database.relationPersonSessionSubjectDao(), session),
|
||||
)
|
||||
|
||||
/**
|
|
@ -1,15 +1,15 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
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
|
||||
|
||||
|
||||
class ClassApi(
|
||||
class ClassDatabaseApi(
|
||||
dao: BaseTaskDao<ClassEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<ClassEntity>(
|
||||
) : BaseTaskDatabaseApi<ClassEntity>(
|
||||
dao,
|
||||
session,
|
||||
ClassEntity::class.java
|
|
@ -1,15 +1,15 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
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
|
||||
|
||||
|
||||
class PersonApi(
|
||||
class PersonDatabaseApi(
|
||||
dao: BaseTaskDao<PersonEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<PersonEntity>(
|
||||
) : BaseTaskDatabaseApi<PersonEntity>(
|
||||
dao,
|
||||
session,
|
||||
PersonEntity::class.java
|
|
@ -1,15 +1,15 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
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
|
||||
|
||||
|
||||
class RelationClassPersonApi(
|
||||
class RelationClassPersonDatabaseApi(
|
||||
dao: BaseTaskDao<RelationClassPersonEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<RelationClassPersonEntity>(
|
||||
) : BaseTaskDatabaseApi<RelationClassPersonEntity>(
|
||||
dao,
|
||||
session,
|
||||
RelationClassPersonEntity::class.java
|
|
@ -1,15 +1,15 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
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
|
||||
|
||||
|
||||
class RelationPersonSessionSubjectApi(
|
||||
class RelationPersonSessionSubjectDatabaseApi(
|
||||
dao: BaseTaskDao<RelationPersonSessionSubjectEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<RelationPersonSessionSubjectEntity>(
|
||||
) : BaseTaskDatabaseApi<RelationPersonSessionSubjectEntity>(
|
||||
dao,
|
||||
session,
|
||||
RelationPersonSessionSubjectEntity::class.java
|
|
@ -1,14 +1,14 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
|
||||
import com.faraphel.tasks_valider.database.entities.SessionEntity
|
||||
|
||||
|
||||
class SessionApi(
|
||||
class SessionDatabaseApi(
|
||||
dao: BaseTaskDao<SessionEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<SessionEntity>(
|
||||
) : BaseTaskDatabaseApi<SessionEntity>(
|
||||
dao,
|
||||
session,
|
||||
SessionEntity::class.java
|
|
@ -1,15 +1,15 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
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
|
||||
|
||||
|
||||
class SubjectApi(
|
||||
class SubjectDatabaseApi(
|
||||
dao: BaseTaskDao<SubjectEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<SubjectEntity>(
|
||||
) : BaseTaskDatabaseApi<SubjectEntity>(
|
||||
dao,
|
||||
session,
|
||||
SubjectEntity::class.java
|
|
@ -1,15 +1,15 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
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
|
||||
|
||||
|
||||
class TaskApi(
|
||||
class TaskDatabaseApi(
|
||||
dao: BaseTaskDao<TaskEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<TaskEntity>(
|
||||
) : BaseTaskDatabaseApi<TaskEntity>(
|
||||
dao,
|
||||
session,
|
||||
TaskEntity::class.java
|
|
@ -1,15 +1,15 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities
|
||||
package com.faraphel.tasks_valider.database.api.server.entities
|
||||
|
||||
import com.faraphel.tasks_valider.database.api.entities.base.BaseTaskApi
|
||||
import com.faraphel.tasks_valider.database.api.server.entities.base.BaseTaskDatabaseApi
|
||||
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
|
||||
|
||||
|
||||
class ValidationApi(
|
||||
class ValidationDatabaseApi(
|
||||
dao: BaseTaskDao<ValidationEntity>,
|
||||
session: SessionEntity
|
||||
) : BaseTaskApi<ValidationEntity>(
|
||||
) : BaseTaskDatabaseApi<ValidationEntity>(
|
||||
dao,
|
||||
session,
|
||||
ValidationEntity::class.java
|
|
@ -1,11 +1,11 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities.base
|
||||
package com.faraphel.tasks_valider.database.api.server.entities.base
|
||||
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
|
||||
/**
|
||||
* A base for the API to handle the database operations with an HTTP server.
|
||||
*/
|
||||
interface BaseApi {
|
||||
interface BaseDatabaseApi {
|
||||
/**
|
||||
* Handle the HEAD request
|
||||
* This is used to check if a data exists in the database
|
|
@ -1,4 +1,4 @@
|
|||
package com.faraphel.tasks_valider.database.api.entities.base
|
||||
package com.faraphel.tasks_valider.database.api.server.entities.base
|
||||
|
||||
import com.faraphel.tasks_valider.database.dao.base.BaseTaskDao
|
||||
import com.faraphel.tasks_valider.database.entities.SessionEntity
|
||||
|
@ -7,14 +7,11 @@ import com.faraphel.tasks_valider.utils.parser
|
|||
import fi.iki.elonen.NanoHTTPD
|
||||
|
||||
|
||||
abstract class BaseTaskApi<Entity> (
|
||||
abstract class BaseTaskDatabaseApi<Entity> (
|
||||
private val dao: BaseTaskDao<Entity>,
|
||||
private val session: SessionEntity,
|
||||
private val entityType: Class<Entity>,
|
||||
) : BaseApi {
|
||||
private fun parseJson(data: String): Entity =
|
||||
parser.fromJson(data, entityType)
|
||||
|
||||
) : BaseDatabaseApi {
|
||||
/**
|
||||
* Handle an HTTP HEAD request.
|
||||
* Indicate if an object exist in the database.
|
||||
|
@ -25,7 +22,7 @@ abstract class BaseTaskApi<Entity> (
|
|||
// get the content of the request
|
||||
val data = httpSession.getBody()
|
||||
// parse the object
|
||||
val obj = this.parseJson(data)
|
||||
val obj = parser.fromJson(data, entityType)
|
||||
// check if the object is in the object accessible from the session
|
||||
val exists = this.dao.getAllBySession(session.id).contains(obj)
|
||||
|
|
@ -35,13 +35,28 @@ import java.time.Instant
|
|||
]
|
||||
)
|
||||
data class ValidationEntity (
|
||||
@ColumnInfo("date") val date: Instant,
|
||||
|
||||
@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("date") val date: Instant,
|
||||
) : BaseEntity() {
|
||||
companion object {
|
||||
const val TABLE_NAME = "validations"
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ValidationEntity. Automatically set the date to today.
|
||||
*/
|
||||
constructor(
|
||||
teacherId: Long,
|
||||
studentId: Long,
|
||||
taskId: Long
|
||||
):
|
||||
this(
|
||||
teacherId = teacherId,
|
||||
studentId = studentId,
|
||||
taskId = taskId,
|
||||
date = Instant.now()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
package com.faraphel.tasks_valider.database.entities.base
|
||||
|
||||
open class BaseEntity
|
||||
open class BaseEntity {
|
||||
companion object {
|
||||
const val TABLE_NAME = "<Undefined>"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package com.faraphel.tasks_valider.database.entities.error
|
||||
|
||||
|
||||
class HttpException(
|
||||
private val code: Int,
|
||||
) : Exception("Http Exception: $code")
|
|
@ -24,7 +24,7 @@ fun populateTaskDatabaseTest(database: TaskDatabase) {
|
|||
) = database.personDao().insert(
|
||||
PersonEntity(
|
||||
"Billy", "Bob",
|
||||
null,
|
||||
"0A1A7553-9DE5-103C-B23C-630998207116",
|
||||
"1234",
|
||||
TaskRole.STUDENT
|
||||
),
|
||||
|
|
|
@ -18,7 +18,6 @@ import com.faraphel.tasks_valider.connectivity.task.TaskClient
|
|||
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.TaskSessionScreen
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
|
|
|
@ -14,10 +14,7 @@ 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
|
||||
|
@ -33,8 +30,7 @@ import com.faraphel.tasks_valider.database.populateSubjectSessionPersonTest
|
|||
import com.faraphel.tasks_valider.ui.screen.authentification.AuthentificationServerScreen
|
||||
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.TaskSessionScreen
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import com.faraphel.tasks_valider.ui.screen.task.TaskSessionController
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
|
@ -70,7 +66,7 @@ fun CommunicationInternetServerScreen(
|
|||
else controller.navigate("session")
|
||||
}
|
||||
composable("session") {
|
||||
TaskSessionScreen(
|
||||
TaskSessionController(
|
||||
activity,
|
||||
client.value!!,
|
||||
adminPersonEntity.value!!
|
||||
|
|
|
@ -19,7 +19,7 @@ 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.bwf.BwfManager
|
||||
import com.faraphel.tasks_valider.connectivity.bwd.BwdManager
|
||||
import com.faraphel.tasks_valider.database.TaskDatabase
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.internet.CommunicationInternetSelectScreen
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.CommunicationWifiP2pScreen
|
||||
|
@ -49,8 +49,8 @@ fun CommunicationModeSelectionScreen(activity: Activity, database: TaskDatabase)
|
|||
CommunicationInternetSelectScreen(activity, database)
|
||||
}
|
||||
composable("wifi-p2p") {
|
||||
val bwfManager = BwfManager.fromActivity(activity)
|
||||
CommunicationWifiP2pScreen(activity, bwfManager)
|
||||
val bwdManager = BwdManager.fromActivity(activity)
|
||||
CommunicationWifiP2pScreen(activity, bwdManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ fun CommunicationModeSelectionScreen(activity: Activity, database: TaskDatabase)
|
|||
*/
|
||||
@Composable
|
||||
fun CommunicationSelectContent(controller: NavController, activity: Activity) {
|
||||
val isWifiP2pSupported = BwfManager.isSupported(activity)
|
||||
val isWifiP2pSupported = BwdManager.isSupported(activity)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
|
|
@ -8,12 +8,12 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager
|
||||
import com.faraphel.tasks_valider.connectivity.bwd.BwdManager
|
||||
import com.faraphel.tasks_valider.ui.widgets.connectivity.WifiP2pDeviceListWidget
|
||||
|
||||
|
||||
@Composable
|
||||
fun CommunicationWifiP2pClientScreen(activity: Activity, bwfManager: BwfManager) {
|
||||
fun CommunicationWifiP2pClientScreen(activity: Activity, bwdManager: BwdManager) {
|
||||
val selectedDevice = remember { mutableStateOf<WifiP2pDevice?>(null) }
|
||||
val isConnected = remember { mutableStateOf(false) }
|
||||
|
||||
|
@ -31,25 +31,25 @@ fun CommunicationWifiP2pClientScreen(activity: Activity, bwfManager: BwfManager)
|
|||
val config = WifiP2pConfig().apply {
|
||||
deviceAddress = selectedDevice.value!!.deviceAddress
|
||||
}
|
||||
bwfManager.connect(config) {
|
||||
bwdManager.connect(config) {
|
||||
isConnected.value = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// display the list of devices
|
||||
CommunicationWifiP2pClientContent(bwfManager, selectedDevice)
|
||||
CommunicationWifiP2pClientContent(bwdManager, selectedDevice)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun CommunicationWifiP2pClientContent(
|
||||
bwfManager: BwfManager,
|
||||
bwdManager: BwdManager,
|
||||
selectedDevice: MutableState<WifiP2pDevice?>
|
||||
) {
|
||||
Column {
|
||||
WifiP2pDeviceListWidget(
|
||||
peers = bwfManager.statePeers.value,
|
||||
peers = bwdManager.statePeers.value,
|
||||
filter = { device: WifiP2pDevice -> device.isGroupOwner },
|
||||
selectedDevice,
|
||||
)
|
||||
|
|
|
@ -9,19 +9,19 @@ 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.bwf.BwfManager
|
||||
import com.faraphel.tasks_valider.connectivity.bwd.BwdManager
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.client.CommunicationWifiP2pClientScreen
|
||||
import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.server.CommunicationWifiP2pServerScreen
|
||||
|
||||
|
||||
@Composable
|
||||
fun CommunicationWifiP2pScreen(activity: Activity, bwfManager: BwfManager) {
|
||||
fun CommunicationWifiP2pScreen(activity: Activity, bwdManager: BwdManager) {
|
||||
val controller = rememberNavController()
|
||||
|
||||
NavHost(navController = controller, startDestination = "mode") {
|
||||
composable("mode") { CommunicationWifiP2pSelectContent(controller) }
|
||||
composable("client") { CommunicationWifiP2pClientScreen(activity, bwfManager) }
|
||||
composable("server") { CommunicationWifiP2pServerScreen(activity, bwfManager) }
|
||||
composable("client") { CommunicationWifiP2pClientScreen(activity, bwdManager) }
|
||||
composable("server") { CommunicationWifiP2pServerScreen(activity, bwdManager) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,33 +1,16 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.server
|
||||
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.room.Room
|
||||
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager
|
||||
import com.faraphel.tasks_valider.connectivity.bwd.BwdManager
|
||||
import com.faraphel.tasks_valider.connectivity.task.TaskClient
|
||||
import com.faraphel.tasks_valider.connectivity.task.TaskServer
|
||||
import com.faraphel.tasks_valider.database.TaskDatabase
|
||||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
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.TaskSessionScreen
|
||||
|
||||
|
||||
@Composable
|
||||
fun CommunicationWifiP2pServerScreen(activity: Activity, bwfManager: BwfManager) {
|
||||
fun CommunicationWifiP2pServerScreen(activity: Activity, bwdManager: BwdManager) {
|
||||
val client = remember { mutableStateOf<TaskClient?>(null) }
|
||||
|
||||
// TODO(Faraphel): fix and get a user
|
||||
|
@ -41,7 +24,7 @@ fun CommunicationWifiP2pServerScreen(activity: Activity, bwfManager: BwfManager)
|
|||
@Composable
|
||||
fun CommunicationWifiP2pServerContent(
|
||||
activity: Activity,
|
||||
bwfManager: BwfManager,
|
||||
bwdManager: BwdManager,
|
||||
client: MutableState<TaskClient?>
|
||||
) {
|
||||
/*
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.scan.qr
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.*
|
||||
|
@ -17,6 +19,11 @@ import com.journeyapps.barcodescanner.DefaultDecoderFactory
|
|||
@Composable
|
||||
fun ScanBarcodeScreen(activity: Activity, barcode: MutableState<BarcodeResult?>) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
// check and prompt for the camera permission
|
||||
if (activity.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
|
||||
// FIXME(Faraphel): seem to crash the application
|
||||
activity.requestPermissions(arrayOf(Manifest.permission.CAMERA), 1)
|
||||
|
||||
// AndroidView is used because "DecoratedBarcodeView" only support the legacy view system
|
||||
AndroidView(factory = {
|
||||
DecoratedBarcodeView(activity).apply {
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.task
|
||||
|
||||
import android.app.Activity
|
||||
import android.widget.Toast
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.navigation.NavController
|
||||
import com.faraphel.tasks_valider.connectivity.task.TaskClient
|
||||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
import com.faraphel.tasks_valider.database.entities.ValidationEntity
|
||||
import com.faraphel.tasks_valider.ui.screen.scan.qr.ScanBarcodeScreen
|
||||
import com.journeyapps.barcodescanner.BarcodeResult
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
|
||||
|
||||
@Composable
|
||||
fun QuickValidationScreen(
|
||||
controller: NavController,
|
||||
activity: Activity,
|
||||
client: TaskClient,
|
||||
user: PersonEntity,
|
||||
) {
|
||||
val barcode = remember { mutableStateOf<BarcodeResult?>(null) }
|
||||
|
||||
// prompt for the qr code if not found
|
||||
if (barcode.value == null)
|
||||
return ScanBarcodeScreen(activity, barcode)
|
||||
|
||||
// show the content of the qr code
|
||||
val studentUrl = barcode.value!!.text.toHttpUrl()
|
||||
val cardId = studentUrl.pathSegments[0]
|
||||
|
||||
// when the barcode changed
|
||||
LaunchedEffect(cardId) {
|
||||
Thread {
|
||||
quickValidation(
|
||||
controller,
|
||||
activity,
|
||||
client,
|
||||
user,
|
||||
cardId,
|
||||
)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the latest task of a user from its student card
|
||||
*/
|
||||
fun quickValidation(
|
||||
controller: NavController,
|
||||
activity: Activity,
|
||||
client: TaskClient,
|
||||
user: PersonEntity,
|
||||
cardId: String,
|
||||
) {
|
||||
// action when an error occurred or everything worked
|
||||
fun finish() {
|
||||
activity.runOnUiThread {
|
||||
// go back to the main screen
|
||||
controller.navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
// requests all the persons
|
||||
val allPersons = client.personApi.getAll()
|
||||
// get the person with the matching card
|
||||
val person = allPersons.firstOrNull { person -> person.cardId == cardId }
|
||||
|
||||
if (person == null) {
|
||||
// tell to the user that this card is linked to nobody
|
||||
activity.runOnUiThread {
|
||||
Toast.makeText(activity, "No person found for that card.", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
return finish()
|
||||
}
|
||||
|
||||
// requests all the relation persons - subjects
|
||||
val allRelationsPersonSubject = client.relationPersonSessionSubjectApi.getAll()
|
||||
// get the corresponding relation
|
||||
val relationPersonSubject = allRelationsPersonSubject.first { relation -> relation.studentId == person.id }
|
||||
|
||||
// requests all the tasks
|
||||
val allTasks = client.taskApi.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()
|
||||
// get the corresponding relation
|
||||
val validations = allValidations.filter { validation -> validation.studentId == person.id }
|
||||
|
||||
// get the first task without any validation
|
||||
val task = tasks.firstOrNull { task ->
|
||||
// check in all the validations if the task is found
|
||||
val validation = validations.firstOrNull { validation -> validation.taskId == task.id }
|
||||
// keep the task if it has no validation
|
||||
validation == null
|
||||
}
|
||||
|
||||
if (task == null) {
|
||||
// tell to the user the action cannot be done
|
||||
activity.runOnUiThread {
|
||||
Toast.makeText(activity, "There are no tasks left.", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
return finish()
|
||||
}
|
||||
|
||||
// create a new validation on the server
|
||||
client.validationApi.save(
|
||||
ValidationEntity(
|
||||
teacherId=user.id,
|
||||
studentId=person.id,
|
||||
taskId=task.id,
|
||||
)
|
||||
)
|
||||
// confirm to the user the action was successful
|
||||
activity.runOnUiThread {
|
||||
Toast.makeText(activity, "Validated \"${task.title}\".", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
return finish()
|
||||
}
|
|
@ -1,24 +1,44 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.task
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
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.runtime.*
|
||||
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
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.faraphel.tasks_valider.connectivity.task.TaskClient
|
||||
import com.faraphel.tasks_valider.database.entities.PersonEntity
|
||||
import com.faraphel.tasks_valider.utils.parser
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
||||
|
||||
@Composable
|
||||
fun TaskSessionController(
|
||||
activity: Activity,
|
||||
client: TaskClient,
|
||||
user: PersonEntity,
|
||||
) {
|
||||
val controller = rememberNavController()
|
||||
|
||||
NavHost(
|
||||
navController = controller,
|
||||
startDestination = "main"
|
||||
) {
|
||||
composable("main") {
|
||||
TaskSessionScreen(controller, activity, client, user)
|
||||
}
|
||||
composable("quick_validation") {
|
||||
QuickValidationScreen(controller, activity, client, user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -28,6 +48,7 @@ import com.google.gson.reflect.TypeToken
|
|||
*/
|
||||
@Composable
|
||||
fun TaskSessionScreen(
|
||||
controller: NavController,
|
||||
activity: Activity,
|
||||
client: TaskClient,
|
||||
user: PersonEntity,
|
||||
|
@ -37,7 +58,9 @@ fun TaskSessionScreen(
|
|||
|
||||
// if the groups are not yet defined, refresh the list
|
||||
if (students.value == null)
|
||||
return Thread { refreshStudents(activity, client, students) }.start()
|
||||
return LaunchedEffect(true) {
|
||||
Thread { refreshStudents(activity, client, students) }.start()
|
||||
}
|
||||
|
||||
if (selectedStudent.value != null)
|
||||
return TaskStudentScreen(
|
||||
|
@ -69,6 +92,17 @@ fun TaskSessionScreen(
|
|||
Text(text = student.fullName())
|
||||
}
|
||||
}
|
||||
|
||||
// separator
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// buttons
|
||||
Row {
|
||||
// quick validation
|
||||
Button(onClick = { controller.navigate("quick_validation") }) {
|
||||
Text("Quick Validation")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,16 +113,14 @@ fun refreshStudents(
|
|||
client: TaskClient,
|
||||
students: MutableState<List<PersonEntity>?>
|
||||
) {
|
||||
// try to obtain the list of groups
|
||||
val response = client.get("entities/" + PersonEntity.TABLE_NAME)
|
||||
|
||||
// in case of error, notify it
|
||||
if (!response.isSuccessful)
|
||||
return activity.runOnUiThread { Toast.makeText(activity, response.message, Toast.LENGTH_LONG).show() }
|
||||
|
||||
// parse the list of groups
|
||||
students.value = parser.fromJson(
|
||||
response.body.string(),
|
||||
object : TypeToken<List<PersonEntity>>(){}.type
|
||||
)
|
||||
try {
|
||||
// try to get all the persons in that session
|
||||
students.value = client.personApi.getAll()
|
||||
} catch (exception: Exception) {
|
||||
// in case of error, show a message
|
||||
return activity.runOnUiThread {
|
||||
Log.e("students", "$exception")
|
||||
Toast.makeText(activity, "Could not retrieve students.\n\n$exception", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import com.faraphel.tasks_valider.database.entities.*
|
|||
import com.faraphel.tasks_valider.utils.dateTimeFormatter
|
||||
import com.faraphel.tasks_valider.utils.parser
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
/**
|
||||
|
@ -95,7 +94,6 @@ fun TaskStudentScreen(
|
|||
client,
|
||||
state,
|
||||
validation ?: ValidationEntity(
|
||||
date=Instant.now(),
|
||||
teacherId=user.id,
|
||||
studentId=student.id,
|
||||
taskId=task.id,
|
||||
|
@ -127,58 +125,26 @@ fun refreshTasksValidations(
|
|||
validations: MutableState<List<ValidationEntity>?>,
|
||||
) {
|
||||
// try to obtain the list of subject
|
||||
val responseSubjects = client.get("entities/" + RelationPersonSessionSubjectEntity.TABLE_NAME)
|
||||
|
||||
// in case of error, notify it
|
||||
if (!responseSubjects.isSuccessful)
|
||||
return activity.runOnUiThread { Toast.makeText(activity, responseSubjects.message, Toast.LENGTH_LONG).show() }
|
||||
|
||||
// parse the list of subjects
|
||||
val allPersonSessionSubjects = parser.fromJson<List<RelationPersonSessionSubjectEntity>>(
|
||||
responseSubjects.body.string(),
|
||||
object : TypeToken<List<RelationPersonSessionSubjectEntity>>(){}.type
|
||||
)
|
||||
|
||||
val allRelationsPersonSessionSubject = client.relationPersonSessionSubjectApi.getAll()
|
||||
// get the subject that the student is using
|
||||
val relationPersonSessionSubjects = allPersonSessionSubjects.firstOrNull { relation -> relation.studentId == student.id }
|
||||
val relationPersonSessionSubject = allRelationsPersonSessionSubject.firstOrNull { relation ->
|
||||
relation.studentId == student.id
|
||||
}
|
||||
|
||||
if (relationPersonSessionSubjects == null)
|
||||
if (relationPersonSessionSubject == null)
|
||||
// TODO(Faraphel): should be able to assign a subject ?
|
||||
return activity.runOnUiThread { Toast.makeText(activity, "No subject assigned", Toast.LENGTH_LONG).show() }
|
||||
|
||||
// try to obtain the list of tasks
|
||||
val responseTasks = client.get("entities/" + TaskEntity.TABLE_NAME)
|
||||
|
||||
// in case of error, notify it
|
||||
if (!responseTasks.isSuccessful)
|
||||
return activity.runOnUiThread { Toast.makeText(activity, responseTasks.message, Toast.LENGTH_LONG).show() }
|
||||
|
||||
// parse the list of subjects
|
||||
val allTasks = parser.fromJson<List<TaskEntity>>(
|
||||
responseTasks.body.string(),
|
||||
object : TypeToken<List<TaskEntity>>(){}.type
|
||||
)
|
||||
val allTasks = client.taskApi.getAll()
|
||||
|
||||
// get the tasks that are linked to this subject
|
||||
tasks.value = allTasks.filter { task ->
|
||||
task.subjectId == relationPersonSessionSubjects.subjectId
|
||||
}.sortedBy { task ->
|
||||
task.order
|
||||
}
|
||||
tasks.value = allTasks
|
||||
.filter { task -> task.subjectId == relationPersonSessionSubject.subjectId }
|
||||
.sortedBy { task -> task.order }
|
||||
|
||||
// try to obtain the list of validations
|
||||
val responseValidations = client.get("entities/" + ValidationEntity.TABLE_NAME)
|
||||
|
||||
// in case of error, notify it
|
||||
if (!responseValidations.isSuccessful)
|
||||
return activity.runOnUiThread { Toast.makeText(activity, responseTasks.message, Toast.LENGTH_LONG).show() }
|
||||
|
||||
// parse the list of validations
|
||||
val allValidations = parser.fromJson<List<ValidationEntity>>(
|
||||
responseValidations.body.string(),
|
||||
object : TypeToken<List<ValidationEntity>>(){}.type
|
||||
)
|
||||
|
||||
val allValidations = client.validationApi.getAll()
|
||||
// filter only the interesting validations
|
||||
validations.value = allValidations.filter { validation ->
|
||||
validation.studentId == student.id &&
|
||||
|
@ -188,20 +154,10 @@ fun refreshTasksValidations(
|
|||
|
||||
|
||||
fun updateValidation(client: TaskClient, checked: Boolean, validation: ValidationEntity) {
|
||||
if (checked) {
|
||||
if (checked)
|
||||
// if the validation is not set, create it
|
||||
client.post(
|
||||
"entities/" + ValidationEntity.TABLE_NAME,
|
||||
parser.toJson(validation),
|
||||
"application/json"
|
||||
)
|
||||
}
|
||||
else {
|
||||
client.validationApi.save(validation)
|
||||
else
|
||||
// if the validation is set, delete it
|
||||
client.delete(
|
||||
"entities/" + ValidationEntity.TABLE_NAME,
|
||||
parser.toJson(validation),
|
||||
"application/json"
|
||||
)
|
||||
}
|
||||
}
|
||||
client.validationApi.delete(validation)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.faraphel.tasks_valider.database.converters
|
||||
package com.faraphel.tasks_valider.utils.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.google.gson.*
|
|
@ -1,6 +1,6 @@
|
|||
package com.faraphel.tasks_valider.utils
|
||||
|
||||
import com.faraphel.tasks_valider.database.converters.InstantConverter
|
||||
import com.faraphel.tasks_valider.utils.converters.InstantConverter
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import java.time.Instant
|
||||
|
|
Loading…
Reference in a new issue