added a helper to improve the WiFi-Direct handling and prototyped the connection screen
This commit is contained in:
parent
adab37fd49
commit
23e39c026a
8 changed files with 252 additions and 118 deletions
|
@ -4,32 +4,21 @@ import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.net.wifi.p2p.*
|
import android.net.wifi.p2p.*
|
||||||
import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener
|
|
||||||
import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
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 androidx.compose.material3.Button
|
import com.faraphel.tasks_valider.connectivity.wifi_p2p.WifiP2pHelper
|
||||||
import com.faraphel.tasks_valider.connectivity.DemoClient
|
|
||||||
import com.faraphel.tasks_valider.connectivity.DemoServer
|
|
||||||
import com.faraphel.tasks_valider.connectivity.wifi_p2p.WifiP2pBroadcastReceiver
|
|
||||||
import com.faraphel.tasks_valider.database.Database
|
import com.faraphel.tasks_valider.database.Database
|
||||||
import java.net.InetSocketAddress
|
import com.faraphel.tasks_valider.ui.widgets.WifiP2pWidgetDevice
|
||||||
import java.net.ServerSocket
|
|
||||||
import java.net.Socket
|
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private var PERMISSION_ACCESS_FINE_LOCATION = 1001 ///< permission code for the fine location (required for WiFi-Direct)
|
private var PERMISSION_ACCESS_FINE_LOCATION = 1001 ///< permission code for the fi
|
||||||
|
private var p2pHelper: WifiP2pHelper? = null ///< the Wi-Fi Direct helper
|
||||||
private var p2pManager: WifiP2pManager? = null ///< the wifi direct manager
|
|
||||||
private var p2pChannel: WifiP2pManager.Channel? = null ///< the wifi direct channel
|
|
||||||
private var p2pReceiver: BroadcastReceiver? = null ///< the wifi direct event receiver handler
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private lateinit var database: Database ///< the database manager
|
private lateinit var database: Database ///< the database manager
|
||||||
|
@ -53,77 +42,40 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the WiFi-Direct manager
|
// get the WiFi-Direct manager
|
||||||
this.p2pManager = this.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
|
val p2pManager: WifiP2pManager? = this.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager?
|
||||||
if (this.p2pManager == null) {
|
if (p2pManager == null) {
|
||||||
Log.e("wifi-p2p", "cannot access the WiFi-Direct manager")
|
Log.e("wifi-p2p", "cannot access the WiFi-Direct manager")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the Wifi-Direct channel
|
// get the Wifi-Direct channel
|
||||||
this.p2pChannel = this.p2pManager!!.initialize(this, this.mainLooper, null)
|
val p2pChannel: WifiP2pManager.Channel = p2pManager.initialize(this, this.mainLooper, null)
|
||||||
if (this.p2pManager == null) {
|
|
||||||
Log.e("wifi-p2p", "cannot access the WiFi-Direct channel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a handler for the WiFi-Direct events
|
// create a helper for handling the WiFi-Direct
|
||||||
this.p2pReceiver = WifiP2pBroadcastReceiver(this.p2pManager!!, this.p2pChannel!!)
|
this.p2pHelper = WifiP2pHelper(p2pManager, p2pChannel)
|
||||||
|
|
||||||
// start a demo connexion
|
// try to display the different devices when available.
|
||||||
this.demoP2pConnexion()
|
this.p2pHelper!!.registerListener(
|
||||||
}
|
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION,
|
||||||
|
{
|
||||||
|
peers: WifiP2pDeviceList? ->
|
||||||
|
|
||||||
private fun demoP2pConnexion() {
|
Log.d("wifi-p2p", "peers: $peers")
|
||||||
// Get the connection information (for the host IP)
|
if (peers != null) this.setContent {
|
||||||
this.p2pManager!!.requestConnectionInfo(this.p2pChannel) { connection ->
|
for (device in peers.deviceList) {
|
||||||
|
Log.d("wifi-p2p", "device: ${device.deviceAddress}")
|
||||||
Log.v("wifi-p2p", "connection information: $connection")
|
WifiP2pWidgetDevice(device)
|
||||||
|
}
|
||||||
// Get the group information (for the owner)
|
|
||||||
this.p2pManager!!.requestGroupInfo(this.p2pChannel) { group ->
|
|
||||||
|
|
||||||
Log.v("wifi-p2p", "group information: $group")
|
|
||||||
|
|
||||||
if (group.isGroupOwner) {
|
|
||||||
Log.d("wifi-p2p", "mode: server")
|
|
||||||
DemoServer(9876).start()
|
|
||||||
} else {
|
|
||||||
Log.d("wifi-p2p", "mode: client")
|
|
||||||
|
|
||||||
Thread { // NOTE(Faraphel): this thread is started to avoid blocking the main loop
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
|
|
||||||
// TODO(Faraphel): the thread is handled outside the connect block to prevent connection before the server started.
|
|
||||||
// This should be handled in the future by selecting the server before starting the client.
|
|
||||||
p2pManager!!.connect(
|
|
||||||
this.p2pChannel,
|
|
||||||
WifiP2pConfig().apply { deviceAddress = group.owner.deviceAddress },
|
|
||||||
object : WifiP2pManager.ActionListener {
|
|
||||||
override fun onSuccess() {
|
|
||||||
Log.d("wifi-p2p", "peer connection successful !")
|
|
||||||
Thread {
|
|
||||||
try {
|
|
||||||
DemoClient(connection.groupOwnerAddress, 9876).once()
|
|
||||||
} catch(exception: Exception) {
|
|
||||||
Log.e("wifi-p2p", "could not connect to the server", exception)
|
|
||||||
}
|
|
||||||
}.start() // NOTE(Faraphel): this thread is started to avoid networking in main thread
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(reason: Int) {
|
|
||||||
Log.e("wifi-p2p", "could not establish a peer connection. Reason: $reason")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
Thread.sleep(1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
}.start()
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
once = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// discovers the peers to trigger the peer changed event
|
||||||
|
this.p2pHelper!!.discoverPeers(object : WifiP2pManager.ActionListener {
|
||||||
|
override fun onSuccess() { Log.i("wifi-p2p", "discovering successful !") }
|
||||||
|
override fun onFailure(reason: Int) { Log.e("wifi-p2p", "discovering failed. Reason: $reason") }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
@ -132,13 +84,13 @@ class MainActivity : ComponentActivity() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
// enable the WiFi-Direct events
|
// enable the WiFi-Direct events
|
||||||
this.registerReceiver(this.p2pReceiver, WifiP2pBroadcastReceiver.INTENT_FILTER)
|
this.registerReceiver(this.p2pHelper, WifiP2pHelper.ALL_INTENT_FILTER)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
|
|
||||||
// disable the WiFi-Direct events
|
// disable the WiFi-Direct events
|
||||||
this.unregisterReceiver(this.p2pReceiver)
|
this.unregisterReceiver(this.p2pHelper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +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.WifiP2pManager
|
|
||||||
import android.util.Log
|
|
||||||
|
|
||||||
class WifiP2pBroadcastReceiver(
|
|
||||||
private val manager: WifiP2pManager,
|
|
||||||
private val channel: WifiP2pManager.Channel,
|
|
||||||
) : BroadcastReceiver() {
|
|
||||||
companion object {
|
|
||||||
val 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReceive(context: Context?, intent: Intent) {
|
|
||||||
when (intent.action) {
|
|
||||||
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
|
|
||||||
Log.i("wifi-p2p", "Connection Changed : $context, $intent, ${intent.extras}")
|
|
||||||
}
|
|
||||||
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
|
|
||||||
Log.i("wifi-p2p", "Peers Changed : $context, $intent, ${intent.extras}")
|
|
||||||
}
|
|
||||||
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
|
|
||||||
Log.i("wifi-p2p", "State Changed : $context, $intent, ${intent.extras}")
|
|
||||||
}
|
|
||||||
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
|
|
||||||
Log.i("wifi-p2p", "This Device Changed : $context, $intent, ${intent.extras}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around WifiP2pManager::connect
|
||||||
|
*
|
||||||
|
* Connect to another device, allowing for a communication using Sockets
|
||||||
|
*/
|
||||||
|
fun connect(deviceAddress: String, listener: WifiP2pManager.ActionListener? = null) {
|
||||||
|
val config = WifiP2pConfig().apply {
|
||||||
|
this.deviceAddress = deviceAddress
|
||||||
|
}
|
||||||
|
this.manager.connect(this.channel, config, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around WifiP2pManager::requestPeers
|
||||||
|
*
|
||||||
|
* Request the list of peers after a discovery.
|
||||||
|
*/
|
||||||
|
fun requestPeers(listener: WifiP2pManager.PeerListListener? = null) {
|
||||||
|
this.manager.requestPeers(this.channel, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around WifiP2pManager::discoverPeers
|
||||||
|
*
|
||||||
|
* Start discovering peers.
|
||||||
|
* Once founds, the WIFI_P2P_PEERS_CHANGED_ACTION event will be triggered.
|
||||||
|
*/
|
||||||
|
fun discoverPeers(listener: WifiP2pManager.ActionListener? = null) {
|
||||||
|
this.manager.discoverPeers(this.channel, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around WifiP2pManager::requestConnectionInfo
|
||||||
|
*
|
||||||
|
* Obtain information about a connection with another device.
|
||||||
|
*/
|
||||||
|
fun requestConnectionInfo(listener: WifiP2pManager.ConnectionInfoListener? = null) {
|
||||||
|
this.manager.requestConnectionInfo(this.channel, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around WifiP2pManager::requestGroupInfo
|
||||||
|
*
|
||||||
|
* Obtain information about the current group.
|
||||||
|
*/
|
||||||
|
fun requestGroupInfo(listener: WifiP2pManager.GroupInfoListener? = null) {
|
||||||
|
this.manager.requestGroupInfo(this.channel, listener)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
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,
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.faraphel.tasks_valider.connectivity.wifi_p2p.error
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Exception for everything concerning the WifiP2pHelper class
|
||||||
|
*/
|
||||||
|
open class WifiP2pHelperException(
|
||||||
|
override val message: String?
|
||||||
|
) : Exception(message)
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.faraphel.tasks_valider.connectivity.wifi_p2p.error
|
||||||
|
|
||||||
|
class WifiP2pHelperInvalidActionException(
|
||||||
|
action: String
|
||||||
|
) : WifiP2pHelperException("This WiFi-Direct action is not supported : $action")
|
|
@ -14,6 +14,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import com.faraphel.tasks_valider.database.Database
|
import com.faraphel.tasks_valider.database.Database
|
||||||
import com.faraphel.tasks_valider.database.entities.TaskGroup
|
import com.faraphel.tasks_valider.database.entities.TaskGroup
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun WidgetTaskStudent(database: Database, taskStudent: TaskGroup) {
|
fun WidgetTaskStudent(database: Database, taskStudent: TaskGroup) {
|
||||||
val teacherDao = database.teacherDao()
|
val teacherDao = database.teacherDao()
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.faraphel.tasks_valider.ui.widgets
|
||||||
|
|
||||||
|
import android.net.wifi.p2p.WifiP2pDevice
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WifiP2pWidgetDevice(device: WifiP2pDevice) {
|
||||||
|
Column {
|
||||||
|
Row {
|
||||||
|
Text(text = "Name: ")
|
||||||
|
Text(text = device.deviceName)
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Text(text = "Address: ")
|
||||||
|
Text(text = device.deviceAddress)
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
Text(text = "Is Owner: ")
|
||||||
|
Text(text = device.isGroupOwner.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue