[WIP] added support for normal Internet connection additionally to WiFi-Direct and improved UI management

This commit is contained in:
Faraphel 2024-05-04 16:07:47 +02:00
parent 60d5be49f7
commit 2637f2fe8b
20 changed files with 548 additions and 255 deletions

View file

@ -10,12 +10,32 @@
<deviceKey> <deviceKey>
<Key> <Key>
<type value="VIRTUAL_DEVICE_PATH" /> <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> </Key>
</deviceKey> </deviceKey>
</Target> </Target>
</targetSelectedWithDropDown> </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> </State>
</entry> </entry>
</value> </value>

View file

@ -4,7 +4,7 @@
<component name="FrameworkDetectionExcludesConfiguration"> <component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" /> <file type="web" url="file://$PROJECT_DIR$" />
</component> </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" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View file

@ -1,20 +1,14 @@
package com.faraphel.tasks_valider package com.faraphel.tasks_valider
import android.Manifest
import android.annotation.SuppressLint 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.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.faraphel.tasks_valider.connectivity.bwf.BwfManager 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.database.Database
import com.faraphel.tasks_valider.ui.screen.room.RoomScreen import com.faraphel.tasks_valider.ui.screen.communication.CommunicationScreen
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@ -28,41 +22,8 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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 { this.setContent {
RoomScreen(this.bwfManager!!) CommunicationScreen(this)
} }
} }

View file

@ -1,10 +1,14 @@
package com.faraphel.tasks_valider.connectivity.bwf package com.faraphel.tasks_valider.connectivity.bwf
import android.Manifest
import android.app.Activity
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageManager
import android.net.wifi.p2p.* import android.net.wifi.p2p.*
import android.util.Log
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.faraphel.tasks_valider.connectivity.bwf.error.* 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_CONNECTION_CHANGED_ACTION)
addAction(WifiP2pManager.WIFI_P2P_PEERS_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 // 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. * 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 callback: the createGroup listener
* @param onRemoveFailure: error handler for the removeGroup event
* *
* @see WifiP2pManager.createGroup * @see WifiP2pManager.createGroup
* @see WifiP2pManager.removeGroup * @see WifiP2pManager.removeGroup

View file

@ -1,4 +1,4 @@
package com.faraphel.tasks_valider.connectivity.bwf.error package com.faraphel.tasks_valider.connectivity.bwf.error
class BwfNotSupportedException : 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( class RoomClient(
private val address: InetAddress, private val address: InetAddress,
private val port: Int private val port: Int
) { ) : Thread() {
private val server = Socket() 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...") Log.d("room-client", "connecting to the server...")
try { try {
server.connect(InetSocketAddress(address, port), 10_000) server.connect(InetSocketAddress(address, port), 10_000)

View file

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