Http Server / Client communication #7
19 changed files with 294 additions and 373 deletions
|
@ -4,27 +4,18 @@
|
|||
<value>
|
||||
<entry key="app">
|
||||
<State>
|
||||
<multipleDevicesSelectedInDropDown value="true" />
|
||||
<runningDeviceTargetsSelectedWithDialog>
|
||||
<targetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<type value="QUICK_BOOT_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="SERIAL_NUMBER" />
|
||||
<value value="ypee7lnbpv9x7lvs" />
|
||||
<type value="VIRTUAL_DEVICE_PATH" />
|
||||
<value value="$USER_HOME$/.android/avd/Very_Small_API_34_2.avd" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
<Target>
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="SERIAL_NUMBER" />
|
||||
<value value="2XJDU17923000406" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</runningDeviceTargetsSelectedWithDialog>
|
||||
</targetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2024-05-02T14:19:57.253897049Z" />
|
||||
</State>
|
||||
</entry>
|
||||
</value>
|
||||
|
|
|
@ -62,6 +62,7 @@ dependencies {
|
|||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.room:room-ktx:2.6.1")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
||||
implementation("androidx.navigation:navigation-compose:2.7.7")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<!-- SDK -->
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:minSdkVersion="24"
|
||||
tools:ignore="GradleOverrides" />
|
||||
|
||||
<!-- Permissions -->
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
package com.faraphel.tasks_valider
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.net.wifi.p2p.*
|
||||
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.packets.PacketPing
|
||||
import com.faraphel.tasks_valider.connectivity.wifi_p2p.WifiP2pHelper
|
||||
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
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private var PERMISSION_ACCESS_FINE_LOCATION = 1001 ///< permission code for the fi
|
||||
private var p2pHelper: WifiP2pHelper? = null ///< the Wi-Fi Direct helper
|
||||
private var bwfManager: BwfManager? = null ///< the WiFi-Direct helper
|
||||
|
||||
companion object {
|
||||
private lateinit var database: Database ///< the database manager
|
||||
|
@ -29,56 +28,56 @@ class MainActivity : ComponentActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// ----- CONNECTION
|
||||
// TODO(Faraphel): more check on permissions
|
||||
|
||||
// TOOD: 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")
|
||||
return
|
||||
throw BwfNotSupportedException()
|
||||
}
|
||||
|
||||
if (this.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||
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), PERMISSION_ACCESS_FINE_LOCATION)
|
||||
this.requestPermissions(
|
||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||
BwfManager.PERMISSION_ACCESS_FINE_LOCATION
|
||||
)
|
||||
}
|
||||
|
||||
// get the WiFi-Direct manager
|
||||
val p2pManager: WifiP2pManager? = this.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
|
||||
if (p2pManager == null) {
|
||||
val manager = this.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
|
||||
if (manager == null) {
|
||||
Log.e("wifi-p2p", "cannot access the WiFi-Direct manager")
|
||||
return
|
||||
throw BwfNotSupportedException()
|
||||
}
|
||||
|
||||
// get the Wifi-Direct channel
|
||||
val p2pChannel: WifiP2pManager.Channel = p2pManager.initialize(this, this.mainLooper, null)
|
||||
// create a channel for the manager
|
||||
val channel = manager.initialize(this, this.mainLooper, null)
|
||||
|
||||
// create a helper for handling the WiFi-Direct
|
||||
this.p2pHelper = WifiP2pHelper(p2pManager, p2pChannel)
|
||||
// create a new manager for handling the WiFi-Direct
|
||||
this.bwfManager = BwfManager(manager, channel)
|
||||
|
||||
// open the room selection screen
|
||||
this.setContent {
|
||||
RoomScreen(this.p2pHelper!!)
|
||||
RoomScreen(this.bwfManager!!)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
// enable the WiFi-Direct events
|
||||
this.registerReceiver(this.p2pHelper, WifiP2pHelper.ALL_INTENT_FILTER)
|
||||
this.registerReceiver(this.bwfManager, BwfManager.ALL_INTENT_FILTER)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
// 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
|
||||
this.unregisterReceiver(this.bwfManager)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.wifi.p2p.*
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.faraphel.tasks_valider.connectivity.bwf.error.*
|
||||
|
||||
|
||||
/**
|
||||
* A helper to wrap the WiFi-Direct manager, the channel and the events.
|
||||
*
|
||||
* This avoids certain annoying features such as always specifying the channel as the first argument or
|
||||
* handling all the events with the base event system.
|
||||
*
|
||||
* @param manager The WiFi-Direct manager
|
||||
* @param channel The WiFi-Direct channel
|
||||
*/
|
||||
class BwfManager(
|
||||
private var manager: WifiP2pManager,
|
||||
private var channel: WifiP2pManager.Channel,
|
||||
) : BroadcastReceiver() {
|
||||
companion object {
|
||||
var PERMISSION_ACCESS_FINE_LOCATION = 1001 ///< permission code for the fine location
|
||||
var ALL_INTENT_FILTER = IntentFilter().apply {
|
||||
addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
|
||||
addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
|
||||
}
|
||||
}
|
||||
|
||||
// Wrappers
|
||||
|
||||
/**
|
||||
* Connect to another device, allowing for a communication using Sockets
|
||||
* @see WifiP2pManager.connect
|
||||
* @throws SecurityException if the permission has not been given
|
||||
*/
|
||||
@Throws(SecurityException::class)
|
||||
fun connect(config: WifiP2pConfig, callback: () -> Unit = {}) =
|
||||
this.manager.connect(this.channel, config, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfConnectException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
* Request the list of peers after a discovery.
|
||||
* @see WifiP2pManager.requestPeers
|
||||
* @throws SecurityException if the permission has not been given
|
||||
*/
|
||||
@Throws(SecurityException::class)
|
||||
fun requestPeers(callback: (WifiP2pDeviceList) -> Unit = {}) =
|
||||
this.manager.requestPeers(this.channel, callback)
|
||||
|
||||
/**
|
||||
* Start discovering peers.
|
||||
* Once founds, the WIFI_P2P_PEERS_CHANGED_ACTION event will be triggered.
|
||||
* @see WifiP2pManager.discoverPeers
|
||||
* @throws SecurityException if the permission has not been given
|
||||
*/
|
||||
@Throws(SecurityException::class)
|
||||
fun discoverPeers(callback: () -> Unit = {}) =
|
||||
this.manager.discoverPeers(this.channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfDiscoverException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
* Obtain information about a connection with another device.
|
||||
* @see WifiP2pManager.requestConnectionInfo
|
||||
*/
|
||||
fun requestConnectionInfo(callback: (WifiP2pInfo) -> Unit = {}) =
|
||||
this.manager.requestConnectionInfo(this.channel, callback)
|
||||
|
||||
/**
|
||||
* Obtain information about the current group.
|
||||
* @see WifiP2pManager.requestGroupInfo
|
||||
* @throws SecurityException if the permission has not been given
|
||||
*/
|
||||
@Throws(SecurityException::class)
|
||||
fun requestGroupInfo(callback: (WifiP2pGroup?) -> Unit = {}) =
|
||||
this.manager.requestGroupInfo(this.channel, callback)
|
||||
|
||||
/**
|
||||
* Create a new WiFi-Direct group.
|
||||
* @see WifiP2pManager.createGroup
|
||||
* @throws SecurityException if the permission has not been given
|
||||
*/
|
||||
@Throws(SecurityException::class)
|
||||
fun createGroup(callback: () -> Unit = {}) =
|
||||
this.manager.createGroup(this.channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfCreateGroupException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
* Disconnect from the current WiFi-Direct group.
|
||||
* @see WifiP2pManager.removeGroup
|
||||
*/
|
||||
fun removeGroup(callback: () -> Unit = {}) =
|
||||
this.manager.removeGroup(this.channel, object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() { callback() }
|
||||
override fun onFailure(reason: Int) = throw BwfRemoveGroupException(reason)
|
||||
})
|
||||
|
||||
/**
|
||||
* Create a new WiFi-Direct group. If already connected to a group, quit it first.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @see WifiP2pManager.createGroup
|
||||
* @see WifiP2pManager.removeGroup
|
||||
*/
|
||||
fun recreateGroup(callback: () -> Unit = {}) {
|
||||
// get the current group information
|
||||
this.requestGroupInfo { group ->
|
||||
// if a group exist, quit it
|
||||
if (group != null)
|
||||
this.removeGroup { this@BwfManager.createGroup(callback) }
|
||||
else
|
||||
// create the group
|
||||
this.createGroup(callback)
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
val stateConnectionInfo = mutableStateOf<WifiP2pInfo?>(null)
|
||||
val statePeers = mutableStateOf<WifiP2pDeviceList?>(null)
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
// ignore empty intent
|
||||
if (intent == null)
|
||||
return
|
||||
|
||||
// update the action corresponding state
|
||||
when (intent.action) {
|
||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> this.requestConnectionInfo {
|
||||
connectionInfo -> stateConnectionInfo.value = connectionInfo
|
||||
}
|
||||
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> this.requestPeers {
|
||||
peers -> statePeers.value = peers
|
||||
}
|
||||
}
|
||||
// TODO(Faraphel): implement event dispatcher
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# Better WiFi-Direct (BWD)
|
||||
|
||||
This package contain code to improve the base WiFi-Direct implementation.
|
||||
|
||||
The base have some issue, like an abusive usage of listener, error code and events that make using it
|
||||
very impractical.
|
||||
|
||||
This improved version will instead focus on asynchronous function and exception, allowing for a
|
||||
cleaner linear code instead.
|
||||
|
||||
(Author: https://git.faraphel.fr/Faraphel)
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfConnectException(
|
||||
reason: Int
|
||||
) : BwfException("Cannot connect to the peer. Reason: $reason")
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfCreateGroupException (
|
||||
reason: Int
|
||||
) : BwfException("Could not create the group : $reason")
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfDiscoverException(
|
||||
reason: Int
|
||||
) : BwfException("Could not discover peers : $reason")
|
|
@ -1,9 +1,9 @@
|
|||
package com.faraphel.tasks_valider.connectivity.wifi_p2p.error
|
||||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
|
||||
/**
|
||||
* Base Exception for everything concerning the WifiP2pHelper class
|
||||
*/
|
||||
open class WifiP2pHelperException(
|
||||
open class BwfException(
|
||||
override val message: String?
|
||||
) : Exception(message)
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfInvalidActionException(
|
||||
action: String
|
||||
) : BwfException("This WiFi-Direct action is not supported : $action")
|
|
@ -0,0 +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.")
|
|
@ -0,0 +1,5 @@
|
|||
package com.faraphel.tasks_valider.connectivity.bwf.error
|
||||
|
||||
class BwfRemoveGroupException (
|
||||
reason: Int
|
||||
) : BwfException("Could not remove the group : $reason")
|
|
@ -1,212 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.wifi_p2p
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.wifi.p2p.WifiP2pConfig
|
||||
import android.net.wifi.p2p.WifiP2pDeviceList
|
||||
import android.net.wifi.p2p.WifiP2pManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.faraphel.tasks_valider.connectivity.wifi_p2p.error.WifiP2pHelperInvalidActionException
|
||||
|
||||
|
||||
/**
|
||||
* A helper to wrap the WiFi-Direct manager, the channel and the events.
|
||||
*
|
||||
* This avoids certain annoying features such as always specifying the channel as the first argument or
|
||||
* handling all the events with the base event system.
|
||||
*
|
||||
* @param manager: the WiFi-Direct base manager
|
||||
* @param channel: the WiFi-Direct channel
|
||||
*/
|
||||
class WifiP2pHelper(
|
||||
private val manager: WifiP2pManager,
|
||||
private val channel: WifiP2pManager.Channel,
|
||||
) : BroadcastReceiver() {
|
||||
companion object {
|
||||
/**
|
||||
* This IntentFilter can be used to subscribe the helper to every supported WiFi-Direct events.
|
||||
*/
|
||||
val ALL_INTENT_FILTER = IntentFilter().apply {
|
||||
addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)
|
||||
addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
|
||||
addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
|
||||
addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
/**
|
||||
* Contains the listeners to call when the peers are updated.
|
||||
*/
|
||||
private val listenersPeersChanged = mutableListOf<WifiP2pHelperListenerInfo<(WifiP2pDeviceList?) -> Unit>>()
|
||||
|
||||
/**
|
||||
* Event receiver for the WiFi-Direct events.
|
||||
* Should not be called manually.
|
||||
* @param context: the WiFi-Direct context
|
||||
* @param intent: the event information
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
|
||||
Log.v("wifi-p2p", "Connection Changed : $context, $intent, ${intent.extras}")
|
||||
}
|
||||
|
||||
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
|
||||
Log.v("wifi-p2p", "Peers Changed : $context, $intent, ${intent.extras}")
|
||||
|
||||
this.requestPeers { peers ->
|
||||
this@WifiP2pHelper.listenersPeersChanged.forEach { listenerInfo -> listenerInfo.callback(peers) }
|
||||
this@WifiP2pHelper.listenersPeersChanged.removeIf { listenerInfo -> listenerInfo.once }
|
||||
}
|
||||
}
|
||||
|
||||
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
|
||||
Log.v("wifi-p2p", "State Changed : $context, $intent, ${intent.extras}")
|
||||
}
|
||||
|
||||
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
|
||||
Log.v("wifi-p2p", "This Device Changed : $context, $intent, ${intent.extras}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener to a specific WiFi-Direct event.
|
||||
* @param action: the name of the WiFi-Direct event
|
||||
* @param callback: the callback function
|
||||
* @param once: should the event be only called once ?
|
||||
*/
|
||||
fun <CallbackType> registerListener(action: String, callback: CallbackType, once: Boolean = false) {
|
||||
when (action) {
|
||||
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
this.listenersPeersChanged.add(WifiP2pHelperListenerInfo(
|
||||
callback as ((WifiP2pDeviceList?) -> Unit),
|
||||
once
|
||||
))
|
||||
}
|
||||
else -> {
|
||||
// raise an exception if the action is invalid
|
||||
throw WifiP2pHelperInvalidActionException(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun <CallbackType> unregisterListener(action: String, callback: CallbackType) {
|
||||
when (action) {
|
||||
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
|
||||
this.listenersPeersChanged.removeIf { listenerInfo -> listenerInfo.callback == callback }
|
||||
}
|
||||
else -> {
|
||||
// raise an exception if the action is invalid
|
||||
throw WifiP2pHelperInvalidActionException(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wrappers
|
||||
|
||||
/**
|
||||
* Connect to another device, allowing for a communication using Sockets
|
||||
* @see WifiP2pManager.connect
|
||||
*/
|
||||
fun connect(config: WifiP2pConfig, listener: WifiP2pManager.ActionListener? = null) {
|
||||
this.manager.connect(this.channel, config, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the list of peers after a discovery.
|
||||
* @see WifiP2pManager.requestPeers
|
||||
*/
|
||||
fun requestPeers(listener: WifiP2pManager.PeerListListener? = null) {
|
||||
this.manager.requestPeers(this.channel, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start discovering peers.
|
||||
* Once founds, the WIFI_P2P_PEERS_CHANGED_ACTION event will be triggered.
|
||||
* @see WifiP2pManager.discoverPeers
|
||||
*/
|
||||
fun discoverPeers(listener: WifiP2pManager.ActionListener? = null) {
|
||||
this.manager.discoverPeers(this.channel, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain information about a connection with another device.
|
||||
* @see WifiP2pManager.requestConnectionInfo
|
||||
*/
|
||||
fun requestConnectionInfo(listener: WifiP2pManager.ConnectionInfoListener? = null) {
|
||||
this.manager.requestConnectionInfo(this.channel, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain information about the current group.
|
||||
* @see WifiP2pManager.requestGroupInfo
|
||||
*/
|
||||
fun requestGroupInfo(listener: WifiP2pManager.GroupInfoListener? = null) {
|
||||
this.manager.requestGroupInfo(this.channel, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WiFi-Direct group.
|
||||
* @see WifiP2pManager.createGroup
|
||||
*/
|
||||
fun createGroup(listener: WifiP2pManager.ActionListener? = null) {
|
||||
this.manager.createGroup(this.channel, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the current WiFi-Direct group.
|
||||
* @see WifiP2pManager.removeGroup
|
||||
*/
|
||||
fun removeGroup(listener: WifiP2pManager.ActionListener? = null) {
|
||||
this.manager.removeGroup(this.channel, listener)
|
||||
}
|
||||
|
||||
// Shortcuts
|
||||
|
||||
/**
|
||||
* Create a new WiFi-Direct group. If already connected to a group, quit it first.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @see WifiP2pManager.createGroup
|
||||
* @see WifiP2pManager.removeGroup
|
||||
*/
|
||||
fun recreateGroup(
|
||||
listener: WifiP2pManager.ActionListener? = null,
|
||||
onRemoveFailure: ((Int) -> Unit)? = null
|
||||
) {
|
||||
// get the current group information
|
||||
this.requestGroupInfo { group ->
|
||||
// if a group exist, quit it
|
||||
if (group != null)
|
||||
this.removeGroup(object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() {
|
||||
// once left, create the group
|
||||
this@WifiP2pHelper.createGroup(listener)
|
||||
}
|
||||
|
||||
override fun onFailure(reason: Int) {
|
||||
// call the handler
|
||||
onRemoveFailure?.invoke(reason)
|
||||
}
|
||||
|
||||
})
|
||||
else
|
||||
// create the group
|
||||
this.createGroup(listener)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.wifi_p2p
|
||||
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
|
||||
/**
|
||||
* This class contain the information required to store a listener in the WifiP2pHelper class to correctly handle events.
|
||||
* @param CallbackType: the type of the callback function
|
||||
* @param callback: the function to call when the event is triggered
|
||||
* @param once: should the listener be called only once ?
|
||||
*/
|
||||
class WifiP2pHelperListenerInfo<CallbackType>(
|
||||
val callback: CallbackType,
|
||||
val once: Boolean,
|
||||
)
|
|
@ -1,5 +0,0 @@
|
|||
package com.faraphel.tasks_valider.connectivity.wifi_p2p.error
|
||||
|
||||
class WifiP2pHelperInvalidActionException(
|
||||
action: String
|
||||
) : WifiP2pHelperException("This WiFi-Direct action is not supported : $action")
|
|
@ -3,32 +3,50 @@ 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 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.connectivity.wifi_p2p.WifiP2pHelper
|
||||
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(wifiP2pHelper: WifiP2pHelper) {
|
||||
val peers = remember { mutableStateOf<WifiP2pDeviceList?>(null) }
|
||||
fun RoomClientScreen(bwfManager: BwfManager) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
val selectedDevice = remember { mutableStateOf<WifiP2pDevice?>(null) }
|
||||
val connected = remember { mutableStateOf(false) }
|
||||
|
||||
// if the device is connected to a host, display the main screen
|
||||
if (connected.value) {
|
||||
TaskGroupScreen()
|
||||
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
|
||||
|
@ -37,53 +55,17 @@ fun RoomClientScreen(wifiP2pHelper: WifiP2pHelper) {
|
|||
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
|
||||
)
|
||||
// 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
|
||||
}
|
||||
|
||||
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")
|
||||
// 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,
|
||||
{ newPeers: WifiP2pDeviceList? -> peers.value = newPeers },
|
||||
)
|
||||
wifiP2pHelper.discoverPeers()
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package com.faraphel.tasks_valider.ui.screen.room
|
||||
|
||||
import android.net.wifi.p2p.WifiP2pManager
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
|
@ -10,8 +9,9 @@ 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.connectivity.bwf.BwfManager
|
||||
import com.faraphel.tasks_valider.ui.screen.tasks.TaskGroupScreen
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.net.ServerSocket
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ var DEFAULT_SERVER_PORT: Int = 9876
|
|||
* This screen allow the user to create a room
|
||||
*/
|
||||
@Composable
|
||||
fun RoomHostScreen(wifiP2pHelper: WifiP2pHelper) {
|
||||
fun RoomHostScreen(bwfManager: BwfManager) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
val serverPort = remember { mutableStateOf(DEFAULT_SERVER_PORT) }
|
||||
val isCreated = remember { mutableStateOf(false) }
|
||||
|
@ -65,23 +65,19 @@ fun RoomHostScreen(wifiP2pHelper: WifiP2pHelper) {
|
|||
|
||||
Button(onClick = {
|
||||
// create a new WiFi-Direct group
|
||||
wifiP2pHelper.recreateGroup(object : WifiP2pManager.ActionListener {
|
||||
override fun onSuccess() {
|
||||
Log.i("room", "group created !")
|
||||
bwfManager.recreateGroup {
|
||||
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()
|
||||
}
|
||||
// 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
|
||||
}
|
||||
override fun onFailure(reason: Int) {
|
||||
Log.e("room", "error while creating group. Reason: $reason")
|
||||
}
|
||||
})
|
||||
// mark the group as created
|
||||
isCreated.value = true
|
||||
}
|
||||
}) {
|
||||
Text("Create")
|
||||
}
|
||||
|
|
|
@ -6,42 +6,35 @@ 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.wifi_p2p.WifiP2pHelper
|
||||
|
||||
|
||||
enum class RoomScreenType {
|
||||
HOST,
|
||||
CLIENT
|
||||
}
|
||||
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(wifiP2pHelper: WifiP2pHelper) {
|
||||
val roomMode = remember { mutableStateOf<RoomScreenType?>(null) }
|
||||
fun RoomScreen(bwfManager: BwfManager) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
when (roomMode.value) {
|
||||
RoomScreenType.CLIENT -> {
|
||||
// Client mode
|
||||
RoomClientScreen(wifiP2pHelper)
|
||||
}
|
||||
RoomScreenType.HOST -> {
|
||||
// Host mode
|
||||
RoomHostScreen(wifiP2pHelper)
|
||||
}
|
||||
|
||||
null -> {
|
||||
// let the user decide which room mode he which to use
|
||||
NavHost(navController = navController, startDestination = "pickMode") {
|
||||
composable(route = "pickMode") {
|
||||
Column {
|
||||
Button(onClick = { roomMode.value = RoomScreenType.CLIENT }) {
|
||||
Button(onClick = { navController.navigate("client") }) {
|
||||
Text(text = "Join a room")
|
||||
}
|
||||
Button(onClick = { roomMode.value = RoomScreenType.HOST }) {
|
||||
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