[WIP] added support for normal Internet connection additionally to WiFi-Direct and improved UI management
This commit is contained in:
parent
60d5be49f7
commit
2637f2fe8b
20 changed files with 548 additions and 255 deletions
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.")
|
|
@ -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.")
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
val client = server.accept()
|
||||
client.soTimeout = timeout // set the timeout for the communication
|
||||
this.handleClient(client)
|
||||
}
|
||||
}.start()
|
||||
override fun run() {
|
||||
while (!server.isClosed) {
|
||||
val client = server.accept()
|
||||
client.soTimeout = timeout // set the timeout for the communication
|
||||
this.handleClient(client)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the server
|
||||
*/
|
||||
fun close() {
|
||||
server.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue