Http Server / Client communication #7

Merged
faraphel merged 24 commits from test-http into main 2024-05-17 17:22:56 +02:00
20 changed files with 548 additions and 255 deletions
Showing only changes of commit 2637f2fe8b - Show all commits

View file

@ -10,12 +10,32 @@
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Very_Small_API_34_2.avd" />
<value value="C:\Users\RC606\.android\avd\Small_Phone_API_26.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2024-05-02T14:19:57.253897049Z" />
<timeTargetWasSelectedWithDropDown value="2024-05-04T10:43:32.941497Z" />
<targetsSelectedWithDialog>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="C:\Users\RC606\.android\avd\Small_Phone_API_26.avd" />
</Key>
</deviceKey>
</Target>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="C:\Users\RC606\.android\avd\Small_Phone_API_26_-_2.avd" />
</Key>
</deviceKey>
</Target>
</targetsSelectedWithDialog>
</State>
</entry>
</value>

View file

@ -4,7 +4,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="Kotlin SDK" project-jdk-type="KotlinSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -1,20 +1,14 @@
package com.faraphel.tasks_valider
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.net.wifi.p2p.WifiP2pManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager
import com.faraphel.tasks_valider.connectivity.bwf.error.BwfNotSupportedException
import com.faraphel.tasks_valider.database.Database
import com.faraphel.tasks_valider.ui.screen.room.RoomScreen
import com.faraphel.tasks_valider.ui.screen.communication.CommunicationScreen
class MainActivity : ComponentActivity() {
@ -28,41 +22,8 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// TODO(Faraphel): more check on permissions
// check if the system support WiFi-Direct
if (!this.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
Log.e("wifi-p2p", "this device does not support the WiFi-Direct feature")
throw BwfNotSupportedException()
}
if (
this.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
this.checkSelfPermission(Manifest.permission.NEARBY_WIFI_DEVICES) != PackageManager.PERMISSION_GRANTED
) {
// TODO(Faraphel): should be used with shouldShowRequestPermissionRationale, with a check
this.requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
BwfManager.PERMISSION_ACCESS_FINE_LOCATION
)
}
// get the WiFi-Direct manager
val manager = this.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
if (manager == null) {
Log.e("wifi-p2p", "cannot access the WiFi-Direct manager")
throw BwfNotSupportedException()
}
// create a channel for the manager
val channel = manager.initialize(this, this.mainLooper, null)
// create a new manager for handling the WiFi-Direct
this.bwfManager = BwfManager(manager, channel)
// open the room selection screen
this.setContent {
RoomScreen(this.bwfManager!!)
CommunicationScreen(this)
}
}

View file

@ -1,10 +1,14 @@
package com.faraphel.tasks_valider.connectivity.bwf
import android.Manifest
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
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.*
@ -28,6 +32,44 @@ class BwfManager(
addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
}
fun isSupported(context: Context): Boolean {
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)
}
/**
* Create a new BwfManager from an activity.
* @param activity The activity to create the manager from
*/
fun fromActivity(activity: Activity): BwfManager {
// 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()
}
// TODO(Faraphel): more check on permissions
if (
activity.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
activity.checkSelfPermission(Manifest.permission.NEARBY_WIFI_DEVICES) != PackageManager.PERMISSION_GRANTED
) {
// TODO(Faraphel): should be used with shouldShowRequestPermissionRationale, with a check
activity.requestPermissions(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
PERMISSION_ACCESS_FINE_LOCATION
)
}
// get the WiFi-Direct manager
val manager = activity.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
?: throw BwfPermissionException()
// get the WiFi-Direct channel
val channel = manager.initialize(activity, activity.mainLooper, null)
return BwfManager(manager, channel)
// NOTE(Faraphel): the broadcast receiver should be registered in the activity onResume
}
}
// Wrappers
@ -109,8 +151,7 @@ class BwfManager(
*
* Note: most of the failure on removal are caused by not having a group already created, which is checked.
*
* @param listener: the createGroup listener
* @param onRemoveFailure: error handler for the removeGroup event
* @param callback: the createGroup listener
*
* @see WifiP2pManager.createGroup
* @see WifiP2pManager.removeGroup

View file

@ -1,4 +1,4 @@
package com.faraphel.tasks_valider.connectivity.bwf.error
class BwfNotSupportedException :
BwfException("WiFi-Direct is not supported on this device, or it is disabled.")
BwfException("WiFi-Direct is not supported on this device.")

View file

@ -0,0 +1,4 @@
package com.faraphel.tasks_valider.connectivity.bwf.error
class BwfPermissionException :
BwfException("WiFi-Direct requires permissions to work properly. Please grant the permissions.")

View file

@ -16,10 +16,12 @@ import java.net.Socket
class RoomClient(
private val address: InetAddress,
private val port: Int
) {
) : Thread() {
private val server = Socket()
fun start() {
constructor(address: String, port: Int) : this(InetAddress.getByName(address), port)
override fun run() {
Log.d("room-client", "connecting to the server...")
try {
server.connect(InetSocketAddress(address, port), 10_000)

View file

@ -14,7 +14,7 @@ import java.net.Socket
class RoomServer(
private val port: Int,
private val timeout: Int = 10_000
) {
) : Thread() {
private var server = ServerSocket(port)
init {
@ -38,13 +38,18 @@ class RoomServer(
/**
* Accept connections and treat them
*/
fun start() {
Thread {
while (true) {
override fun run() {
while (!server.isClosed) {
val client = server.accept()
client.soTimeout = timeout // set the timeout for the communication
this.handleClient(client)
}
}.start()
}
/**
* Close the server
*/
fun close() {
server.close()
}
}

View file

@ -0,0 +1,64 @@
package com.faraphel.tasks_valider.ui.screen.communication.internet.client
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.text.input.KeyboardType
import com.faraphel.tasks_valider.connectivity.room.RoomClient
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.tasks.TaskGroupScreen
@Composable
fun CommunicationInternetClientScreen() {
val client = remember { mutableStateOf<RoomClient?>(null) }
if (client.value == null) CommunicationInternetClientContent(client)
else TaskGroupScreen()
}
@Composable
fun CommunicationInternetClientContent(client: MutableState<RoomClient?>) {
val serverAddress = remember { mutableStateOf(DEFAULT_SERVER_ADDRESS) }
val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) }
Column {
// server address
TextField(
value = serverAddress.value,
onValueChange = { text ->
serverAddress.value = text
}
)
// server port
TextField(
value = serverPort.intValue.toString(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
onValueChange = { text ->
val port = text.toInt()
if (port in RANGE_SERVER_PORT) {
serverPort.intValue = port
}
}
)
Button(onClick = {
// TODO(Faraphel): check if the server is reachable
client.value = RoomClient(serverAddress.value, serverPort.intValue)
client.value!!.start()
}) {
Text("Connect")
}
}
}

View file

@ -0,0 +1,45 @@
package com.faraphel.tasks_valider.ui.screen.communication.internet
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.faraphel.tasks_valider.ui.screen.communication.internet.client.CommunicationInternetClientScreen
import com.faraphel.tasks_valider.ui.screen.communication.internet.server.CommunicationInternetServerScreen
@Composable
fun CommunicationInternetScreen() {
val controller = rememberNavController()
NavHost(navController = controller, startDestination = "mode") {
composable("mode") {
CommunicationInternetSelectContent(controller)
}
composable("client") {
CommunicationInternetClientScreen()
}
composable("server") {
CommunicationInternetServerScreen()
}
}
}
@Composable
fun CommunicationInternetSelectContent(controller: NavController) {
Column {
// client mode
Button(onClick = { controller.navigate("client") }) {
Text("Client")
}
// server mode
Button(onClick = { controller.navigate("server") }) {
Text("Server")
}
}
}

View file

@ -0,0 +1,79 @@
package com.faraphel.tasks_valider.ui.screen.communication.internet.server
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 com.faraphel.tasks_valider.connectivity.bwf.BwfManager
import com.faraphel.tasks_valider.connectivity.room.RoomServer
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.tasks.TaskGroupScreen
@Composable
fun CommunicationInternetServerScreen() {
val server = remember { mutableStateOf<RoomServer?>(null)}
// if the server is not created, prompt the user for the server configuration
if (server.value == null) CommunicationInternetServerContent(server)
// else, go to the base tasks screen
else TaskGroupScreen()
}
@Composable
fun CommunicationInternetServerContent(server: MutableState<RoomServer?>) {
val expandedStudentList = remember { mutableStateOf(false) }
val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) }
Column {
// student list
Button(onClick = { expandedStudentList.value = !expandedStudentList.value }) {
Text(text = "Select Students List")
}
DropdownMenu(
expanded = expandedStudentList.value,
onDismissRequest = { expandedStudentList.value = false }
) {
DropdownMenuItem(
text = { Text("ISRI") },
onClick = {}
)
DropdownMenuItem(
text = { Text("MIAGE") },
onClick = {}
)
// TODO(Faraphel): student lists should be loaded from the database or a file
}
// server port
TextField(
value = serverPort.intValue.toString(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
onValueChange = { text ->
val port = text.toInt()
if (port in RANGE_SERVER_PORT) {
serverPort.intValue = port
}
}
)
Button(onClick = {
server.value = RoomServer(serverPort.intValue)
server.value!!.start()
// TODO(Faraphel): go to the base tasks screen
}) {
Text("Create")
}
}
}

View file

@ -0,0 +1,77 @@
package com.faraphel.tasks_valider.ui.screen.communication
import android.app.Activity
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
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.ui.screen.communication.internet.CommunicationInternetScreen
import com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.CommunicationWifiP2pScreen
/**
* CommunicationController is the main controller for the communication screen.
* It is responsible for handling the navigation between the different communication methods.
* It is also responsible for initializing the communication methods.
* @param activity: The activity that hosts the communication screen.
*/
@Composable
fun CommunicationScreen(activity: Activity) {
val controller = rememberNavController()
NavHost(
navController = controller,
startDestination = "select"
) {
composable("select") {
CommunicationSelectContent(controller, activity)
}
composable("internet") {
CommunicationInternetScreen()
}
composable("wifi-p2p") {
val bwfManager = BwfManager.fromActivity(activity)
CommunicationWifiP2pScreen(bwfManager)
}
}
}
/**
* Communication screen that allows the user to choose the communication mode
*/
@Composable
fun CommunicationSelectContent(controller: NavController, activity: Activity) {
val isWifiP2pSupported = BwfManager.isSupported(activity)
Column {
// internet communication mode
Button(onClick = { controller.navigate("internet") }) {
Text("Internet")
}
// wifi-direct communication mode
Button(
colors = ButtonDefaults.buttonColors(
// if the WiFi-Direct is not supported, the button is grayed out
containerColor = if (isWifiP2pSupported) Color.Unspecified else Color.Gray
),
onClick = {
// if the WiFi-Direct is supported, navigate to the WiFi-Direct screen
if (isWifiP2pSupported) controller.navigate("wifi-p2p")
// if the WiFi-Direct is not supported, show a toast message
else Toast.makeText(activity, "WiFi-Direct is not supported on this device", Toast.LENGTH_SHORT).show()
}
) {
Text("WiFi-Direct")
}
}
}

View file

@ -0,0 +1,6 @@
package com.faraphel.tasks_valider.ui.screen.communication
const val DEFAULT_SERVER_ADDRESS: String = "127.0.0.1"
const val DEFAULT_SERVER_PORT: Int = 9876
val RANGE_SERVER_PORT: IntRange = 1024..65535

View file

@ -0,0 +1,55 @@
package com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.client
import android.net.wifi.p2p.WifiP2pConfig
import android.net.wifi.p2p.WifiP2pDevice
import androidx.compose.foundation.layout.Column
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.ui.screen.tasks.TaskGroupScreen
import com.faraphel.tasks_valider.ui.widgets.connectivity.WifiP2pDeviceListWidget
@Composable
fun CommunicationWifiP2pClientScreen(bwfManager: BwfManager) {
val selectedDevice = remember { mutableStateOf<WifiP2pDevice?>(null) }
val isConnected = remember { mutableStateOf(false) }
// if connected, show the task group screen
if (isConnected.value) {
TaskGroupScreen()
return
}
// if the device is selected but not connected, try to connect
if (selectedDevice.value != null) {
// TODO(Faraphel): error handling
val config = WifiP2pConfig().apply {
deviceAddress = selectedDevice.value!!.deviceAddress
}
bwfManager.connect(config) {
isConnected.value = true
}
return
}
// display the list of devices
CommunicationWifiP2pClientContent(bwfManager, selectedDevice)
}
@Composable
fun CommunicationWifiP2pClientContent(
bwfManager: BwfManager,
selectedDevice: MutableState<WifiP2pDevice?>
) {
Column {
WifiP2pDeviceListWidget(
peers = bwfManager.statePeers.value,
filter = { device: WifiP2pDevice -> device.isGroupOwner },
selectedDevice,
)
}
}

View file

@ -0,0 +1,48 @@
package com.faraphel.tasks_valider.ui.screen.communication.wifiP2p
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.ui.screen.communication.internet.CommunicationInternetScreen
import com.faraphel.tasks_valider.ui.screen.communication.internet.server.CommunicationInternetServerScreen
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(bwfManager: BwfManager) {
val controller = rememberNavController()
NavHost(navController = controller, startDestination = "mode") {
composable("mode") {
CommunicationWifiP2pSelectContent(controller)
}
composable("client") {
CommunicationWifiP2pClientScreen(bwfManager)
}
composable("server") {
CommunicationWifiP2pServerScreen(bwfManager)
}
}
}
@Composable
fun CommunicationWifiP2pSelectContent(controller: NavController) {
Column {
// client mode
Button(onClick = { controller.navigate("client") }) {
Text("Client")
}
// server mode
Button(onClick = { controller.navigate("server") }) {
Text("Server")
}
}
}

View file

@ -0,0 +1,83 @@
package com.faraphel.tasks_valider.ui.screen.communication.wifiP2p.server
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 com.faraphel.tasks_valider.connectivity.bwf.BwfManager
import com.faraphel.tasks_valider.connectivity.room.RoomServer
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.tasks.TaskGroupScreen
@Composable
fun CommunicationWifiP2pServerScreen(bwfManager: BwfManager) {
val server = remember { mutableStateOf<RoomServer?>(null)}
// if the server is not created, prompt the user for the server configuration
if (server.value == null) CommunicationWifiP2pServerContent(bwfManager, server)
// else, go to the base tasks screen
else TaskGroupScreen()
}
@Composable
fun CommunicationWifiP2pServerContent(
bwfManager: BwfManager,
server: MutableState<RoomServer?>
) {
val expandedStudentList = remember { mutableStateOf(false) }
val serverPort = remember { mutableIntStateOf(DEFAULT_SERVER_PORT) }
Column {
// student list
Button(onClick = { expandedStudentList.value = !expandedStudentList.value }) {
Text(text = "Select Students List")
}
DropdownMenu(
expanded = expandedStudentList.value,
onDismissRequest = { expandedStudentList.value = false }
) {
DropdownMenuItem(
text = { Text("ISRI") },
onClick = {}
)
DropdownMenuItem(
text = { Text("MIAGE") },
onClick = {}
)
// TODO(Faraphel): student lists should be loaded from the database or a file
}
// server port
TextField(
value = serverPort.intValue.toString(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
onValueChange = { text ->
val port = text.toInt()
if (port in RANGE_SERVER_PORT) {
serverPort.intValue = port
}
}
)
Button(onClick = {
bwfManager.recreateGroup {
server.value = RoomServer(serverPort.intValue)
server.value!!.start()
}
}) {
Text("Create")
}
}
}

View file

@ -1,71 +0,0 @@
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.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 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.room.RoomClient
import com.faraphel.tasks_valider.ui.screen.tasks.TaskGroupScreen
import com.faraphel.tasks_valider.ui.widgets.connectivity.WifiP2pDeviceListWidget
import kotlinx.coroutines.runBlocking
/**
* This screen allow the user to join a room
*/
@Composable
fun RoomClientScreen(bwfManager: BwfManager) {
val navController = rememberNavController()
val selectedDevice = remember { mutableStateOf<WifiP2pDevice?>(null) }
val connected = remember { mutableStateOf(false) }
NavHost(navController = navController, startDestination = "roomSearch") {
composable(route = "roomSearch") {
// let the user pick which host he wishes to connect to
Column {
Text(text = "Find a Server")
// display all the devices that are owner of their group
WifiP2pDeviceListWidget(
peers = bwfManager.statePeers.value,
filter = { device: WifiP2pDevice -> device.isGroupOwner },
selectedDevice,
)
}
// update the list when a new device is detected
}
composable(route = "taskGroup") {
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 to the host
bwfManager.connect(config) {
Log.i("room", "Connection successful to the host !")
bwfManager.requestConnectionInfo { connectionInfo ->
val client = RoomClient(connectionInfo.groupOwnerAddress, 9876) // TODO(Faraphel): port should be a settings
Thread { client.start() }.start()
connected.value = true
}
}
}
}

View file

@ -1,86 +0,0 @@
package com.faraphel.tasks_valider.ui.screen.room
import android.util.Log
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.bwf.BwfManager
import com.faraphel.tasks_valider.ui.screen.tasks.TaskGroupScreen
import kotlinx.coroutines.runBlocking
import java.net.ServerSocket
var DEFAULT_SERVER_PORT: Int = 9876
/**
* This screen allow the user to create a room
*/
@Composable
fun RoomHostScreen(bwfManager: BwfManager) {
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
if (isCreated.value) {
TaskGroupScreen()
return
}
// ask the user settings about the room
Column {
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
bwfManager.recreateGroup {
Log.i("room", "group created !")
// open a new server
val server = ServerSocket(serverPort.value)
Thread {
val client = server.accept() // TODO(Faraphel): should run in a thread
client.getInputStream()
}
// mark the group as created
isCreated.value = true
}
}) {
Text("Create")
}
}
}

View file

@ -1,40 +0,0 @@
package com.faraphel.tasks_valider.ui.screen.room
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager
/**
* This screen will allow the user to choose which room is he connecting to, or let him create one.
*/
@Composable
fun RoomScreen(bwfManager: BwfManager) {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "pickMode") {
composable(route = "pickMode") {
Column {
Button(onClick = { navController.navigate("client") }) {
Text(text = "Join a room")
}
Button(onClick = { navController.navigate("host") }) {
Text(text = "Create a room")
}
}
}
composable(route = "client") {
RoomClientScreen(bwfManager)
}
composable(route = "host") {
RoomHostScreen(bwfManager)
}
}
}