From ddb63a47ed471b857446284c8e9990daef65fe5d Mon Sep 17 00:00:00 2001 From: Faraphel Date: Thu, 2 May 2024 12:46:50 +0200 Subject: [PATCH] [WIP] added a packet system with the network server and client --- .idea/deploymentTargetDropDown.xml | 16 +-- .idea/kotlinc.xml | 2 +- .idea/misc.xml | 6 +- .idea/uiDesigner.xml | 124 ++++++++++++++++++ app/build.gradle.kts | 10 +- .../faraphel/tasks_valider/MainActivity.kt | 6 + .../tasks_valider/connectivity/DemoClient.kt | 49 ------- .../tasks_valider/connectivity/DemoServer.kt | 50 ------- .../connectivity/packets/BasePacket.kt | 29 ++++ .../connectivity/packets/PacketPing.kt | 10 ++ .../connectivity/room/RoomClient.kt | 41 ++++++ .../connectivity/room/RoomServer.kt | 50 +++++++ .../ui/screen/room/RoomClientScreen.kt | 62 ++++++++- .../ui/screen/room/RoomHostScreen.kt | 57 ++++++-- .../ui/screen/tasks/TaskGroupScreen.kt | 1 + .../connectivity/WifiP2pDeviceListWidget.kt | 20 ++- .../connectivity/WifiP2pDeviceWidget.kt | 44 +++++-- .../ui/widgets/{ => tasks}/Group.kt | 2 +- .../ui/widgets/{ => tasks}/Task.kt | 2 +- .../ui/widgets/{ => tasks}/TaskGroup.kt | 2 +- build.gradle.kts | 5 +- 21 files changed, 438 insertions(+), 150 deletions(-) create mode 100644 .idea/uiDesigner.xml delete mode 100644 app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoClient.kt delete mode 100644 app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoServer.kt create mode 100644 app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/BasePacket.kt create mode 100644 app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/PacketPing.kt create mode 100644 app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomClient.kt create mode 100644 app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomServer.kt rename app/src/main/java/com/faraphel/tasks_valider/ui/widgets/{ => tasks}/Group.kt (88%) rename app/src/main/java/com/faraphel/tasks_valider/ui/widgets/{ => tasks}/Task.kt (86%) rename app/src/main/java/com/faraphel/tasks_valider/ui/widgets/{ => tasks}/TaskGroup.kt (97%) diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index d68bcc3..665b551 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -5,26 +5,26 @@ - + - + - - + + - + - - + + - + diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fdf8d99..fe63bb6 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8978d23..f9d6e36 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,10 @@ + - + + + + diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 998a95c..2bfae1d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,7 +1,8 @@ plugins { id("com.android.application") - id("org.jetbrains.kotlin.android") id("com.google.devtools.ksp") + kotlin("android") + kotlin("plugin.serialization") } android { @@ -41,7 +42,7 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.5.1" + kotlinCompilerExtensionVersion = "1.5.13" } packaging { resources { @@ -51,15 +52,16 @@ android { } dependencies { - implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.core:core-ktx:1.13.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") - implementation("androidx.activity:activity-compose:1.8.2") + implementation("androidx.activity:activity-compose:1.9.0") implementation(platform("androidx.compose:compose-bom:2023.08.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") implementation("androidx.room:room-ktx:2.6.1") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/java/com/faraphel/tasks_valider/MainActivity.kt b/app/src/main/java/com/faraphel/tasks_valider/MainActivity.kt index 3689afc..56cbc1c 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/MainActivity.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/MainActivity.kt @@ -11,6 +11,7 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.annotation.RequiresApi +import com.faraphel.tasks_valider.connectivity.packets.PacketPing import com.faraphel.tasks_valider.connectivity.wifi_p2p.WifiP2pHelper import com.faraphel.tasks_valider.database.Database import com.faraphel.tasks_valider.ui.screen.room.RoomScreen @@ -75,4 +76,9 @@ class MainActivity : ComponentActivity() { // disable the WiFi-Direct events this.unregisterReceiver(this.p2pHelper) } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + // TODO(Faraphel): save the current state to reload it later + } } diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoClient.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoClient.kt deleted file mode 100644 index c33ebe0..0000000 --- a/app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoClient.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.faraphel.tasks_valider.connectivity - -import android.util.Log -import java.net.InetAddress -import java.net.InetSocketAddress -import java.net.Socket - - -class DemoClient(hostname: InetAddress, port: Int) { - companion object { - var SOCKET_TIMEOUT = 120 * 1000 ///< TEST(Faraphel): long timeout for testing - } - - private var server = Socket() - private var address = InetSocketAddress(hostname, port) - - init { - server.soTimeout = SOCKET_TIMEOUT - } - - /** - * Try to contact the server once - */ - fun once() { - Log.d("demo-client", "connecting to the server...") - this.server.connect(this.address, SOCKET_TIMEOUT) - Log.d("demo-client", "connected !") - - Log.d("demo-client", "sending message...") - server.getOutputStream().write(42) // send a byte - Log.d("demo-client", "message sent !") - } - - /** - * Try to contact the server indefinitely - */ - fun start() { - Thread { - while (true) { - try { - this.once() - } catch (exception: Exception) { - Log.w("demo-client", "an error occured: $exception") - } - Thread.sleep(1000) - } - }.start() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoServer.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoServer.kt deleted file mode 100644 index 5f0aa31..0000000 --- a/app/src/main/java/com/faraphel/tasks_valider/connectivity/DemoServer.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.faraphel.tasks_valider.connectivity - -import android.util.Log -import java.net.ServerSocket - - -class DemoServer(port: Int) { - - companion object { - var SOCKET_TIMEOUT = 120 * 1000 ///< TEST(Faraphel): long timeout for testing - } - - private var server = ServerSocket(port) - - init { - server.soTimeout = SOCKET_TIMEOUT - } - - /** - * Accept and treat a client once - */ - private fun once() { - // wait for a client connexion - Log.d("demo-server", "waiting for a client...") - val client = server.accept() - Log.d("demo-server", "client connected !") - - // once connected, wait and read a byte from the client. - val value = client.getInputStream().read() - Log.d("demo-server", "client sent data : $value") - - // TODO(Faraphel): an abstraction layer should be added in the future to receive type of packets and read the data - } - - /** - * Accept and threat clients indefinitely - */ - fun start() { - Thread { - while (true) { - try { - this.once() - } catch (exception: Exception) { - Log.w("demo-server", "an error occured: $exception") - } - Thread.sleep(1000) - } - }.start() - } -} diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/BasePacket.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/BasePacket.kt new file mode 100644 index 0000000..6b5a297 --- /dev/null +++ b/app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/BasePacket.kt @@ -0,0 +1,29 @@ +package com.faraphel.tasks_valider.connectivity.packets + +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json + + +/** + * Base for a packet that can be encoded and decoded for a Socket + */ +@Serializable +sealed class BasePacket { + companion object { + /** + * Create a new instance from an array of bytes. + * @param data: data obtained from the toBytes function. + */ + inline fun fromBytes(data: ByteArray): Packet { + return Json.decodeFromString(data.toString()) + } + } + + /** + * Encode the content of the packet into an array of bytes + */ + fun toBytes(): ByteArray { + return Json.encodeToString(this).encodeToByteArray() + } +} diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/PacketPing.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/PacketPing.kt new file mode 100644 index 0000000..3d641b2 --- /dev/null +++ b/app/src/main/java/com/faraphel/tasks_valider/connectivity/packets/PacketPing.kt @@ -0,0 +1,10 @@ +package com.faraphel.tasks_valider.connectivity.packets + +import kotlinx.serialization.Serializable + + +/** + * This is a simple packet class to test a connection + */ +@Serializable +data object PacketPing : BasePacket() diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomClient.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomClient.kt new file mode 100644 index 0000000..3431bbe --- /dev/null +++ b/app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomClient.kt @@ -0,0 +1,41 @@ +package com.faraphel.tasks_valider.connectivity.room + +import android.util.Log +import com.faraphel.tasks_valider.connectivity.packets.BasePacket +import com.faraphel.tasks_valider.connectivity.packets.PacketPing +import java.net.InetAddress +import java.net.InetSocketAddress +import java.net.Socket + + +/** + * A client to handle the room connection. + * @param address the address of the server + * @param port the port of the server + */ +class RoomClient( + private val address: InetAddress, + private val port: Int +) { + private val server = Socket() + + fun start() { + Log.d("room-client", "connecting to the server...") + try { + server.connect(InetSocketAddress(address, port), 10_000) + } catch (exception: Exception) { + Log.e("room-client", "could not connect to the server", exception) + return + } + Log.d("room-client", "connection successful !") + + val serverIn = server.getInputStream() + val serverOut = server.getOutputStream() + + serverOut.write(PacketPing.toBytes()) + val data = serverIn.readBytes() + val packet = BasePacket.fromBytes(data) + + Log.d("room-client", packet.toString()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomServer.kt b/app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomServer.kt new file mode 100644 index 0000000..4e51489 --- /dev/null +++ b/app/src/main/java/com/faraphel/tasks_valider/connectivity/room/RoomServer.kt @@ -0,0 +1,50 @@ +package com.faraphel.tasks_valider.connectivity.room + +import android.util.Log +import com.faraphel.tasks_valider.connectivity.packets.PacketPing +import java.net.ServerSocket +import java.net.Socket + + +/** + * A server to handle the room connection. + * @param port the port of the server + * @param timeout the timeout for a client (in milliseconds) + */ +class RoomServer( + private val port: Int, + private val timeout: Int = 10_000 +) { + private var server = ServerSocket(port) + + init { + server.soTimeout = 0 // accepting clients take an infinite timeout + } + + /** + * Accept and treat a client + */ + private fun handleClient(client: Socket) { + // TODO(Faraphel): should every client be handled in a new small thread ? + // Create the thread here and handle it until the connection is broken + + val clientIn = client.getInputStream() + val clientOut = client.getOutputStream() + + Log.i("room-server", "data: ${PacketPing.toBytes().toList()}") + clientOut.write(PacketPing.toBytes()) + } + + /** + * Accept connections and treat them + */ + fun start() { + Thread { + while (true) { + val client = server.accept() + client.soTimeout = timeout // set the timeout for the communication + this.handleClient(client) + } + }.start() + } +} diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomClientScreen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomClientScreen.kt index 6bc878c..681d4c8 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomClientScreen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomClientScreen.kt @@ -1,13 +1,19 @@ package com.faraphel.tasks_valider.ui.screen.room +import android.net.wifi.p2p.WifiP2pConfig +import android.net.wifi.p2p.WifiP2pDevice import android.net.wifi.p2p.WifiP2pDeviceList +import android.net.wifi.p2p.WifiP2pInfo import android.net.wifi.p2p.WifiP2pManager +import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import com.faraphel.tasks_valider.connectivity.room.RoomClient import com.faraphel.tasks_valider.connectivity.wifi_p2p.WifiP2pHelper +import com.faraphel.tasks_valider.ui.screen.tasks.TaskGroupScreen import com.faraphel.tasks_valider.ui.widgets.connectivity.WifiP2pDeviceListWidget @@ -17,17 +23,67 @@ import com.faraphel.tasks_valider.ui.widgets.connectivity.WifiP2pDeviceListWidge @Composable fun RoomClientScreen(wifiP2pHelper: WifiP2pHelper) { val peers = remember { mutableStateOf(null) } + val selectedDevice = remember { mutableStateOf(null) } + val connected = remember { mutableStateOf(false) } + + // if the device is connected to a host, display the main screen + if (connected.value) { + TaskGroupScreen() + } + + // if a device is selected, connect to it + if (selectedDevice.value != null) { + // configure the connection to point to the selected device + val config = WifiP2pConfig().apply { + deviceAddress = selectedDevice.value!!.deviceAddress + } + // try to connect + wifiP2pHelper.connect(config, object : WifiP2pManager.ActionListener { + override fun onSuccess() { + Log.i("room", "Connection successful to the host !") + // request additional information about the connection to obtain the host IP + wifiP2pHelper.registerListener( + WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION, + { connectionInfo: WifiP2pInfo -> + val client = RoomClient( + connectionInfo.groupOwnerAddress, + 9876 // TODO(Faraphel): port should be a settings + ) + Thread { + client.start() + }.start() + + connected.value = true + }, + once = true + ) + } + + override fun onFailure(reason: Int) { + // TODO(Faraphel): for most of theses messages, shouldn't a toast be used instead ? + Log.e("room", "Could not connect to this host. Reason: $reason") + } + }) + return + } + + // let the user pick which host he wishes to connect to Column { Text(text = "Find a Server") - WifiP2pDeviceListWidget(peers.value) + // display all the devices that are owner of their group + WifiP2pDeviceListWidget( + peers = peers.value, + filter = { device: WifiP2pDevice -> device.isGroupOwner }, + selectedDevice, + ) } // TODO(Faraphel): might be doubtful. Test with more phones, should it only run once ? refresh button ? + // update the list when a new device is detected wifiP2pHelper.registerListener( WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION, - { new_peers: WifiP2pDeviceList? -> peers.value = new_peers }, - once = true + { newPeers: WifiP2pDeviceList? -> peers.value = newPeers }, ) wifiP2pHelper.discoverPeers() } \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomHostScreen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomHostScreen.kt index eb2dcf0..467470b 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomHostScreen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/room/RoomHostScreen.kt @@ -2,22 +2,29 @@ package com.faraphel.tasks_valider.ui.screen.room import android.net.wifi.p2p.WifiP2pManager import android.util.Log -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import com.faraphel.tasks_valider.connectivity.wifi_p2p.WifiP2pHelper import com.faraphel.tasks_valider.ui.screen.tasks.TaskGroupScreen +import java.net.ServerSocket + + +var DEFAULT_SERVER_PORT: Int = 9876 /** * This screen allow the user to create a room */ -@OptIn(ExperimentalMaterial3Api::class) @Composable fun RoomHostScreen(wifiP2pHelper: WifiP2pHelper) { val expanded = remember { mutableStateOf(false) } + val serverPort = remember { mutableStateOf(DEFAULT_SERVER_PORT) } val isCreated = remember { mutableStateOf(false) } // if the group have been created, display the group screen @@ -26,26 +33,48 @@ fun RoomHostScreen(wifiP2pHelper: WifiP2pHelper) { return } + // ask the user settings about the room Column { - ExposedDropdownMenuBox( - expanded = expanded.value, - onExpandedChange = { value -> expanded.value = !value } - ) { - DropdownMenuItem( - text = { Text("ISRI") }, - onClick = {} - ) - DropdownMenuItem( - text = { Text("MIAGE") }, - onClick = {} - ) + Box(Modifier.fillMaxWidth()) { + // dropdown button + Button(onClick = { expanded.value = !expanded.value }) { + Text(text = "Select Students List") + } + // dropdown list + DropdownMenu( + expanded = expanded.value, + onDismissRequest = { expanded.value = false } + ) { + DropdownMenuItem( + text = { Text("ISRI") }, + onClick = {} + ) + DropdownMenuItem( + text = { Text("MIAGE") }, + onClick = {} + ) + } } + // ask the user what will be the server port + TextField( + value = serverPort.value.toString(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + onValueChange = { value -> serverPort.value = value.toIntOrNull() ?: DEFAULT_SERVER_PORT } + ) + Button(onClick = { // create a new WiFi-Direct group wifiP2pHelper.recreateGroup(object : WifiP2pManager.ActionListener { override fun onSuccess() { Log.i("room", "group created !") + + val server = ServerSocket(serverPort.value) // TODO(Faraphel): should the port be a settings ? + Thread { + val client = server.accept() // TODO(Faraphel): should run in a thread + client.getInputStream() + } + // mark the group as created isCreated.value = true } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/tasks/TaskGroupScreen.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/tasks/TaskGroupScreen.kt index 60c4fca..f9b4e26 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/screen/tasks/TaskGroupScreen.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/screen/tasks/TaskGroupScreen.kt @@ -9,5 +9,6 @@ import androidx.compose.runtime.Composable */ @Composable fun TaskGroupScreen() { + // TODO(Faraphel): should handle connexion with the server Text(text = "Task Group") } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceListWidget.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceListWidget.kt index f6e19f1..9c09b82 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceListWidget.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceListWidget.kt @@ -1,22 +1,38 @@ package com.faraphel.tasks_valider.ui.widgets.connectivity +import android.net.wifi.p2p.WifiP2pDevice import android.net.wifi.p2p.WifiP2pDeviceList import androidx.compose.foundation.layout.Column import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState /** * Represent a list of WiFi-Direct devices. + * @param peers the list of peers to represent + * @param filter a filter for the peers + * @param deviceState a state containing the selected device */ @Composable -fun WifiP2pDeviceListWidget(peers: WifiP2pDeviceList?) { +fun WifiP2pDeviceListWidget( + peers: WifiP2pDeviceList?, + filter: ((WifiP2pDevice) -> Boolean)? = null, + deviceState: MutableState? = null, +) { Text(text = "Devices (${peers?.deviceList?.size ?: 0})") Column { + // if there are peers to display if (peers != null) { + // for every device in the list for (device in peers.deviceList) { - WifiP2pDeviceWidget(device) + // if the filter (if set) does not apply to this device, ignore it + if (filter != null && !filter(device)) + continue + + // create a new object for the device + WifiP2pDeviceWidget(device, deviceState) } } } diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceWidget.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceWidget.kt index b0363e0..71e9b70 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceWidget.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/connectivity/WifiP2pDeviceWidget.kt @@ -3,24 +3,42 @@ package com.faraphel.tasks_valider.ui.widgets.connectivity import android.net.wifi.p2p.WifiP2pDevice import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.graphics.Color +/** + * A widget that represent a WiFi-Direct device. + * @param device the device that should be represented + * @param deviceState a state that will be updated to the device when it is selected + */ @Composable -fun WifiP2pDeviceWidget(device: WifiP2pDevice) { - Column { - Row { - Text(text = "Name: ") - Text(text = device.deviceName) - } - Row { - Text(text = "Address: ") - Text(text = device.deviceAddress) - } - Row { - Text(text = "Is Owner: ") - Text(text = device.isGroupOwner.toString()) +fun WifiP2pDeviceWidget(device: WifiP2pDevice, deviceState: MutableState? = null) { + Button(onClick = { if (deviceState != null) deviceState.value = device }) { + Column { + Row { + Text(text = "Name: ") + Text(text = device.deviceName) + } + Row { + Text(text = "Is Owner: ") + Text(text = device.isGroupOwner.toString()) + } + Row { + Text(text = "Address: ", color = Color.LightGray) + Text(text = device.deviceAddress, color = Color.LightGray) + } + Row { + Text(text = "Primary Type: ", color = Color.Green) + Text(text = device.primaryDeviceType, color = Color.Green) + } + Row { + Text(text = "Status: ", color = Color.Yellow) + Text(text = device.status.toString(), color = Color.Yellow) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/Group.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/Group.kt similarity index 88% rename from app/src/main/java/com/faraphel/tasks_valider/ui/widgets/Group.kt rename to app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/Group.kt index 42c2c51..79e5c85 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/Group.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/Group.kt @@ -1,4 +1,4 @@ -package com.faraphel.tasks_valider.ui.widgets +package com.faraphel.tasks_valider.ui.widgets.tasks import androidx.compose.foundation.layout.Column import androidx.compose.material3.Text diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/Task.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/Task.kt similarity index 86% rename from app/src/main/java/com/faraphel/tasks_valider/ui/widgets/Task.kt rename to app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/Task.kt index eddc7fd..67b182f 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/Task.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/Task.kt @@ -1,4 +1,4 @@ -package com.faraphel.tasks_valider.ui.widgets +package com.faraphel.tasks_valider.ui.widgets.tasks import androidx.compose.foundation.layout.Column import androidx.compose.material3.Text diff --git a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/TaskGroup.kt b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/TaskGroup.kt similarity index 97% rename from app/src/main/java/com/faraphel/tasks_valider/ui/widgets/TaskGroup.kt rename to app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/TaskGroup.kt index eb3dd80..99b3fd0 100644 --- a/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/TaskGroup.kt +++ b/app/src/main/java/com/faraphel/tasks_valider/ui/widgets/tasks/TaskGroup.kt @@ -1,4 +1,4 @@ -package com.faraphel.tasks_valider.ui.widgets +package com.faraphel.tasks_valider.ui.widgets.tasks import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/build.gradle.kts b/build.gradle.kts index 5cdb21d..b87154e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id("com.android.application") version "8.2.2" apply false - id("org.jetbrains.kotlin.android") version "1.9.0" apply false id("com.google.devtools.ksp") version "1.9.21-1.0.15" apply false -} \ No newline at end of file + kotlin("android") version "1.9.23" apply false + kotlin("plugin.serialization") version "1.9.23" apply false +}