diff --git a/.idea/misc.xml b/.idea/misc.xml index daa0c6c..9caaea4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4dad71a..051a946 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,6 +28,7 @@ + = 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 - constructor(address: String, port: Int) : this(InetAddress.getByName(address), port) - - override fun run() { - Log.i("room-client", "started !") - - // send a request to the server - val request = okhttp3.Request.Builder() - .url(URL("http://${address.hostAddress}:$port")) - .build() - - // get the response - val response = client.newCall(request).execute() - - // check if the response is successful - if (!response.isSuccessful) { - Log.e("room-client", "could not connect to the server") - return + override fun loadForRequest(url: HttpUrl): List { + return this.cookies + } + override fun saveFromResponse(url: HttpUrl, cookies: List) { + this.cookies.addAll(cookies) + } } - Log.i("room-client", "connected to the server") + ).build() - // parse the response - val body = response.body.string() - val data = jsonParser.fromJson(body, Map::class.java) + // TODO(Faraphel): automatically convert content to the correct type ? - // print the data - data.forEach { (key, value) -> Log.d("room-client", "$key: $value") } - } + /** + * 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() } \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskServer.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskServer.kt index fb17dd3..ba2151a 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskServer.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/connectivity/task/TaskServer.kt @@ -1,6 +1,8 @@ package com.faraphel.tasks_valider.connectivity.task import com.faraphel.tasks_valider.connectivity.task.api.TaskSessionManagerApi +import com.faraphel.tasks_valider.connectivity.task.session.TaskRole +import com.faraphel.tasks_valider.connectivity.task.session.TaskSession import com.faraphel.tasks_valider.connectivity.task.session.TaskSessionManager import com.faraphel.tasks_valider.database.TaskDatabase import com.faraphel.tasks_valider.database.api.TaskDatabaseApi @@ -16,11 +18,36 @@ class TaskServer( private val port: Int, private val database: TaskDatabase ) : NanoHTTPD(port) { + companion object { + private val TASK_SESSION_ADMIN = TaskSession( ///< the admin default session + role = TaskRole.ADMIN + ) + } + private val sessionManager = TaskSessionManager() ///< the session manager + private val adminSessionId = this.sessionManager.newSessionData(TASK_SESSION_ADMIN) ///< default admin session id private val sessionManagerApi = TaskSessionManagerApi(this.sessionManager) ///< the api of the session manager private val databaseApi = TaskDatabaseApi(this.database) ///< the api of the database + /** + * Return a new client that can be used by the admin + */ + fun getClientAdmin(): TaskClient { + // create the session cookie for the admin + val cookieSession = okhttp3.Cookie.Builder() + .domain("localhost") + .name("sessionId") + .value(adminSessionId) + .build() + // create a new client + return TaskClient( + "localhost", + this.port, + listOf(cookieSession) + ) + } + /** * Handle an API request * @param httpSession the http session @@ -30,7 +57,7 @@ class TaskServer( val taskSession = this.sessionManager.getOrCreateSessionData(httpSession) // parse the url - val uri: String = httpSession.uri.substring(1) // remove the first slash + val uri: String = httpSession.uri.trim('/') val path = uri.split("/").toMutableList() // get the type of the request from the uri diff --git a/app/src/main/java/com/faraphel/tasks_valider/database/entities/GroupEntity.kt b/app/src/main/java/com/faraphel/tasks_valider/database/entities/GroupEntity.kt index d042662..81913df 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/database/entities/GroupEntity.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/database/entities/GroupEntity.kt @@ -6,6 +6,7 @@ import androidx.room.PrimaryKey import com.faraphel.tasks_valider.database.entities.base.BaseEntity +// TODO(Faraphel): should be renamed to TeamEntity @Entity(tableName = "groups") data class GroupEntity ( @ColumnInfo("id") @PrimaryKey(autoGenerate = true) val id: Long = 0, diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/client/screen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/client/screen.kt index 7dad218..348c674 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/client/screen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/client/screen.kt @@ -1,5 +1,6 @@ package com.faraphel.tasks_valider.ui.screen.communication.internet.client +import android.app.Activity import androidx.compose.foundation.layout.Column import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Button @@ -19,11 +20,11 @@ import com.faraphel.tasks_valider.ui.screen.task.TaskGroupScreen @Composable -fun CommunicationInternetClientScreen() { +fun CommunicationInternetClientScreen(activity: Activity) { val client = remember { mutableStateOf(null) } if (client.value == null) CommunicationInternetClientContent(client) - else TaskGroupScreen() + else TaskGroupScreen(activity, client.value!!) } @@ -56,7 +57,6 @@ fun CommunicationInternetClientContent(client: MutableState) { Button(onClick = { // TODO(Faraphel): check if the server is reachable client.value = TaskClient(serverAddress.value, serverPort.intValue) - client.value!!.start() }) { Text("Connect") } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/screen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/screen.kt index 4c2455b..335c2f7 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/screen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/screen.kt @@ -6,7 +6,6 @@ import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.navigation.NavController -import androidx.navigation.activity import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController @@ -19,15 +18,9 @@ fun CommunicationInternetScreen(activity: Activity) { val controller = rememberNavController() NavHost(navController = controller, startDestination = "mode") { - composable("mode") { - CommunicationInternetSelectContent(controller) - } - composable("client") { - CommunicationInternetClientScreen() - } - composable("server") { - CommunicationInternetServerScreen(activity) - } + composable("mode") { CommunicationInternetSelectContent(controller) } + composable("client") { CommunicationInternetClientScreen(activity) } + composable("server") { CommunicationInternetServerScreen(activity) } } } @@ -36,12 +29,8 @@ fun CommunicationInternetScreen(activity: Activity) { fun CommunicationInternetSelectContent(controller: NavController) { Column { // client mode - Button(onClick = { controller.navigate("client") }) { - Text("Client") - } + Button(onClick = { controller.navigate("client") }) { Text("Client") } // server mode - Button(onClick = { controller.navigate("server") }) { - Text("Server") - } + Button(onClick = { controller.navigate("server") }) { Text("Server") } } } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server/screen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server/screen.kt index d148633..e0a7649 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server/screen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/internet/server/screen.kt @@ -16,6 +16,7 @@ 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.task.TaskClient import com.faraphel.tasks_valider.connectivity.task.TaskServer import com.faraphel.tasks_valider.database.TaskDatabase import com.faraphel.tasks_valider.ui.screen.communication.DEFAULT_SERVER_PORT @@ -25,17 +26,17 @@ import com.faraphel.tasks_valider.ui.screen.task.TaskGroupScreen @Composable fun CommunicationInternetServerScreen(activity: Activity) { - val server = remember { mutableStateOf(null)} + val client = remember { mutableStateOf(null) } // if the server is not created, prompt the user for the server configuration - if (server.value == null) CommunicationInternetServerContent(activity, server) + if (client.value == null) CommunicationInternetServerContent(activity, client) // else, go to the base tasks screen - else TaskGroupScreen() + else TaskGroupScreen(activity, client.value!!) } @Composable -fun CommunicationInternetServerContent(activity: Activity, server: MutableState) { +fun CommunicationInternetServerContent(activity: Activity, client: MutableState) { val expandedStudentList = remember { mutableStateOf(false) } val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) } @@ -84,8 +85,13 @@ fun CommunicationInternetServerContent(activity: Activity, server: MutableState< // Create the server Log.i("room-server", "creating the server") - server.value = TaskServer(serverPort.intValue, database) - server.value!!.start() + Thread { // a thread is used for networking + val server = TaskServer(serverPort.intValue, database) + server.start() + + // Get the client from the server + client.value = server.getClientAdmin() + }.start() }) { Text("Create") } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/client/screen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/client/screen.kt index 52e4b34..3ba8cdd 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/client/screen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/client/screen.kt @@ -1,5 +1,6 @@ package com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.client +import android.app.Activity import android.net.wifi.p2p.WifiP2pConfig import android.net.wifi.p2p.WifiP2pDevice import androidx.compose.foundation.layout.Column @@ -8,18 +9,18 @@ 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.ui.screen.task.TaskGroupScreen import com.faraphel.tasks_valider.ui.widgets.connectivity.WifiP2pDeviceListWidget @Composable -fun CommunicationWifiP2pClientScreen(bwfManager: BwfManager) { +fun CommunicationWifiP2pClientScreen(activity: Activity, bwfManager: BwfManager) { val selectedDevice = remember { mutableStateOf(null) } val isConnected = remember { mutableStateOf(false) } // if connected, show the task group screen if (isConnected.value) { - TaskGroupScreen() + // TaskGroupScreen(activity, null) + // TODO(Faraphel): finish the connection return } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/screen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/screen.kt index d0ee3f3..bd21fef 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/screen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/screen.kt @@ -19,15 +19,9 @@ fun CommunicationWifiP2pScreen(activity: Activity, bwfManager: BwfManager) { val controller = rememberNavController() NavHost(navController = controller, startDestination = "mode") { - composable("mode") { - CommunicationWifiP2pSelectContent(controller) - } - composable("client") { - CommunicationWifiP2pClientScreen(bwfManager) - } - composable("server") { - CommunicationWifiP2pServerScreen(activity, bwfManager) - } + composable("mode") { CommunicationWifiP2pSelectContent(controller) } + composable("client") { CommunicationWifiP2pClientScreen(activity, bwfManager) } + composable("server") { CommunicationWifiP2pServerScreen(activity, bwfManager) } } } @@ -36,12 +30,8 @@ fun CommunicationWifiP2pScreen(activity: Activity, bwfManager: BwfManager) { fun CommunicationWifiP2pSelectContent(controller: NavController) { Column { // client mode - Button(onClick = { controller.navigate("client") }) { - Text("Client") - } + Button(onClick = { controller.navigate("client") }) { Text("Client") } // server mode - Button(onClick = { controller.navigate("server") }) { - Text("Server") - } + Button(onClick = { controller.navigate("server") }) { Text("Server") } } } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/server/screen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/server/screen.kt index 11125c2..bcb92d3 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/server/screen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/communication/wifiP2p/server/screen.kt @@ -1,6 +1,7 @@ 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 @@ -16,6 +17,7 @@ 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.task.TaskClient import com.faraphel.tasks_valider.connectivity.task.TaskServer import com.faraphel.tasks_valider.database.TaskDatabase import com.faraphel.tasks_valider.ui.screen.communication.DEFAULT_SERVER_PORT @@ -25,12 +27,12 @@ import com.faraphel.tasks_valider.ui.screen.task.TaskGroupScreen @Composable fun CommunicationWifiP2pServerScreen(activity: Activity, bwfManager: BwfManager) { - val server = remember { mutableStateOf(null)} + val client = remember { mutableStateOf(null) } // if the server is not created, prompt the user for the server configuration - if (server.value == null) CommunicationWifiP2pServerContent(activity, bwfManager, server) + if (client.value == null) CommunicationWifiP2pServerContent(activity, bwfManager, client) // else, go to the base tasks screen - else TaskGroupScreen() + else TaskGroupScreen(activity, client.value!!) } @@ -38,7 +40,7 @@ fun CommunicationWifiP2pServerScreen(activity: Activity, bwfManager: BwfManager) fun CommunicationWifiP2pServerContent( activity: Activity, bwfManager: BwfManager, - server: MutableState + client: MutableState ) { val expandedStudentList = remember { mutableStateOf(false) } val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) } @@ -90,8 +92,12 @@ fun CommunicationWifiP2pServerContent( bwfManager.recreateGroup { // Create the server - server.value = TaskServer(serverPort.intValue, database) - server.value!!.start() + Log.i("room-server", "creating the server") + val server = TaskServer(serverPort.intValue, database) + server.start() + + // Get the client from the server + client.value = server.getClientAdmin() } }) { Text("Create") diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/screen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/screen.kt index a8fbfc5..b517118 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/screen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/task/screen.kt @@ -1,14 +1,58 @@ package com.faraphel.tasks_valider.ui.screen.task +import android.app.Activity +import android.widget.Toast 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 com.faraphel.tasks_valider.connectivity.task.TaskClient +import com.faraphel.tasks_valider.database.entities.TaskGroupEntity +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken + + +val jsonParser = Gson() /** * This screen let the user decide which student team he wants to interact with + * @param client an HTTP client that can communicate with the server */ @Composable -fun TaskGroupScreen() { - // TODO(Faraphel): should handle connexion with the server +fun TaskGroupScreen(activity: Activity, client: TaskClient) { + val groups = remember { mutableStateOf?>(null) } + + // title Text(text = "Task Group") + + // if the groups are not yet defined, refresh the list + if (groups.value == null) { + Thread { refreshGroups(activity, client, groups) }.start() + return + } + + // if the groups have already been defined, display them + for (group in groups.value!!) { + Text(text = group.toString()) + } } + + +fun refreshGroups(activity: Activity, client: TaskClient, groups: MutableState?>) { + // try to obtain the list of groups + val response = client.get("entities/group") + + // in case of error, notify it + if (!response.isSuccessful) { + Toast.makeText(activity, response.message, Toast.LENGTH_LONG).show() + return + } + + // parse the list of groups + groups.value = jsonParser.fromJson( + response.body.toString(), + object : TypeToken>(){} + ) +} \ No newline at end of file