In this lab, you will learn how to use Android Studio to create simple Android applications. You will also learn:
Wi-Fi Direct (P2P) allows devices with the appropriate hardware to connect directly to each other via Wi-Fi without an intermediate access point
Popular uses are
The WifiP2pManager class provides methods to enable you to interact with the Wi-Fi hardware on your device to do things like discover and connect to peers. The following actions are available:
Method | Description |
initialize() | Registers the application with the Wi-Fi framework. Call this before calling any other Wi-Fi P2P method. |
connect() | Starts a peer-to-peer connection with a device with the specified configuration. |
cancelConnect() | Cancels any ongoing peer-to-peer group negotiation. |
requestConnectInfo() | Requests a device's connection information. |
createGroup() | Creates a peer-to-peer group with the current device as the group owner. |
removeGroup() | Removes the current peer-to-peer group. |
requestGroupInfo() | Requests peer-to-peer group information. |
discoverPeers() | Initiates peer discovery. |
requestPeers() | Requests the current list of discovered peers. |
Listener interface | Associated actions | Description |
WifiP2pManager.ActionListener | connect(), cancelConnect(), createGroup(), removeGroup(), and discoverPeers() | The ActionListener is an interface that defines 2 functions onSuccess and onFailure that tells you if the call completed successfully or not |
WifiP2pManager.ChannelListener | initialize() | This listener is an interface that notifies when the framework channel is lost |
WifiP2pManager.ConnectionInfoListener | requestConnectInfo() | This listener is an interface that notifies you when information on the connection is available (If the group was formed, address of the GO, if the device is the GO, ) |
WifiP2pManager.GroupInfoListener | requestGroupInfo() | This is an interface that notifies when the group information is available (Get network ID name and password, and whether this device is the GO ) |
WifiP2pManager.PeerListListener | requestPeers() | This is an interface that calls notifies on when the list of devices has updated |
The Wi-Fi P2P APIs define intents that are broadcast when certain Wi-Fi P2P events happen, such as when a new peer is discovered or when a device's Wi-Fi state changes. You can register to receive these intents in your application by creating a broadcast receiver that handles these intents:
Intent | Description |
WIFI_P2P_CONNECTION_CHANGED_ACTION | Broadcast when the state of the device's Wi-Fi connection changes. |
WIFI_P2P_PEERS_CHANGED_ACTION | Broadcast when you call discoverPeers(). You will usually call requestPeers() to get an updated list of peers if you handle this intent in your application. |
WIFI_P2P_STATE_CHANGED_ACTION | Broadcast when Wi-Fi P2P is enabled or disabled on the device. |
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION | Broadcast when a device's details have changed, such as the device's name. |
The objective of this app is to create an echo server. An echo server is one that receives an input from a client, reverses the message and sends the message back to the client. This app will make use of multiple activities to maintain logical separation of concerns
EchoServerWifiDirectAt the END of the lab, the file structure should look like this:
app/src/main
- java+kotlin/com/example/echoserverwifidirect
- chatlist
- ChatListAdapter.kt
- models
- ContentModel.kt
- network
- Client.kt
- NetworkMessageInterface.kt
- Server.kt
- peerlist
- PeerListAdapter.kt
- PeerListAdapterInterface.kt
- wifidirect
- WifiDirectInterface.kt
- WifiDirectManager.kt
PermissionActivity.kt(This is the usual MainActivity)
CommunicationActivity.kt
- res/layout
- activity_communication.xml
- activity_permission.xml
- chat_item.xml
- peer_item.xml
The logic of the application takes into consideration all possible states.

The first section of this lab deals with permissions. We'd create an Activity (Page/Screen) that's dedicated to permissions as the app will not function without it.

In general, the permissions your app is requesting should be listed in the AndroidManifest.xml file
Request permission to use the Wi-Fi hardware on the device and declare your application to have the correct minimum SDK version in the Android manifest:
AndroidManifext.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<!-- Add these lines below -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="android.permission.NEARBY_WIFI_DEVICES"
tools:targetApi="s" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Add the lines above -->
<application
REDACTED FOR BREVITY
Request the permissions of the app. Rename the MainActivity to PermissionActivity by right-clicking > refactor > rename.
The permissions should be checked every time the application is launched in the onCreate. If the app does not have all the permissions, then we should request them from the OS
class MainActivity : AppCompatActivity() {
/// The [requestCode] variable acts as an identifier for the app that's requesting the permissions.
private val requestCode = 1234
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_permission)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
// If we do not have our permissions, we need to request them. Create an array of the permissions we want,
// then send a request to the android OS
if (!hasAllPermissions()){
var perm = arrayOf(
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_FINE_LOCATION,
)
if (SDK_INT >= 33){
// Android 13 (API 33) requires the NEARBY_WIFI_DEVICES permission
perm +=Manifest.permission.NEARBY_WIFI_DEVICES
}
ActivityCompat.requestPermissions(this, perm, requestCode)
} else {
// If i do have permissions, then i can navigate to the next page
navigateToNextPage()
}
}
}
//This is a function inside PermissionActivity
private fun hasAllPermissions():Boolean{
var perm = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(Manifest.permission.ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(Manifest.permission.CHANGE_WIFI_STATE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermission(Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED
if (SDK_INT >= 33){
// If we're running on android SDK 33 or higher, we also need the NEARBY_WIFI_DEVICES permission
perm = perm && checkSelfPermission(Manifest.permission.NEARBY_WIFI_DEVICES) == PackageManager.PERMISSION_GRANTED
}
return perm
}
Handle the callback from the android OS when permissions are granted or denied
/// This function is called by the OS itself after the user interacts with the permissions popups.
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
when(requestCode){
this.requestCode -> {
if (hasAllPermissions()){
navigateToNextPage()
}
}
else -> {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}
}
If a user repeatedly denies the permission to the application, They may be required to manually grant it in the application settings. This would involve the user moving away from the app and then coming back to it. With our current setup, we have no way of capturing this edge case. To solve that lets override the onResume function.
// This is a function inside the PermissionActivity
override fun onResume() {
super.onResume()
if (hasAllPermissions()){
navigateToNextPage()
}
}
The onResume function runs whenever the app is resumed from the background. And thus, with this implementation, will be able to properly redirect the user when the app's permissions are granted using the settings.
// This is a function inside PermissionActivity
fun goToSettings(view: View) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.setData(uri)
startActivity(intent)
}
// This is a function inside PermissionActivity
private fun navigateToNextPage(){
print("I should go to the nex page")
}
activity_main.xml to activity_permission.xml. (And make the change to the line in the onCreate)<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_margin="32dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PermissionActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Permissions"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.4" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="Unfortunately you have not granted the necessary permissions to use this app. Please grant these settings manually using the button below!"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="64dp"
android:onClick="goToSettings"
android:text="Go to Settings"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.526"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now that our permission handling is out of the way, we can focus on the remaining logic. The next step in the process is to handle changes to WifiDirect.
Consider the following questions
In this section, we cater for all of those scenarios in the app.
CommunicationActivity. (right click folder with kotlin files > new > empty views activity)PermissionActivity to navigate to this activity once the conditions are satisfied private fun navigateToNextPage(){
val i = Intent(this,CommunicationActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
startActivity(i)
}
While there are many ways to handle WiFi Direct, we're going to leverage BroadcastReceivers to automatically receive the information from the operating system.
wifidirectWifiDirectManager in that folderWifiDirectInterface in that folderThe WifiDirectManager class will contain the code necessary to perform all WiFi direct operations like
The WifiDirectInterface class will list functions that its implementing member MUST implement. The idea here is that the core functionality will be implemented in the CommunicationActivity. i.e. CommunicationActivity will implement WifiDirectInterface
The information that we're interested in from wifiDirect are as follows
The interface will be defined as follows
interface WifiDirectInterface {
fun onWiFiDirectStateChanged(isEnabled:Boolean)
fun onPeerListUpdated(deviceList: Collection<WifiP2pDevice>)
fun onGroupStatusChanged(groupInfo: WifiP2pGroup?)
fun onDeviceStatusChanged(thisDevice: WifiP2pDevice)
}
The broadcast receiver is responsible for listening to events from the operating system and performing actions accordingly.
Implement the WifiDirectManager class as follows
/// This [WifiDirectManager] class is a [BroadcastReceiver] that listens for events fired from the
/// operating system and relays the relevant information using the [WiFiDirectInterface] to the
/// [CommunicationActivity]
class WifiDirectManager(
private val manager: WifiP2pManager,
private val channel: WifiP2pManager.Channel,
private val iFaceImpl: WifiDirectInterface
):BroadcastReceiver() {
var groupInfo: WifiP2pGroup? = null
@SuppressLint("MissingPermission")
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> {
val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1)
val isWifiP2pEnabled = state == WifiP2pManager.WIFI_P2P_STATE_ENABLED
iFaceImpl.onWiFiDirectStateChanged(isWifiP2pEnabled)
Log.e("WFDManager","The WiFi direct adapter state has changed to $isWifiP2pEnabled")
}
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> {
manager.requestPeers(channel) { peers: WifiP2pDeviceList? ->
peers?.deviceList?.let { iFaceImpl.onPeerListUpdated(it) }
Log.e("WFDManager","The peer listing has changed")
}
}
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> {
val wifiP2pInfo = when{
Build.VERSION.SDK_INT >= 33 -> intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, WifiP2pInfo::class.java)!!
else -> @Suppress("DEPRECATION") intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)!!
}
val tmpGroupInfo = when{
!(wifiP2pInfo.groupFormed)->null
Build.VERSION.SDK_INT >= 33 -> intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, WifiP2pGroup::class.java)!!
else -> @Suppress("DEPRECATION") intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)!!
}
if (groupInfo != tmpGroupInfo){
groupInfo = tmpGroupInfo
Log.e("WFDManager","The group status has changed")
iFaceImpl.onGroupStatusChanged(groupInfo)
}
}
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> {
val thisDevice = when{
Build.VERSION.SDK_INT >= 33 -> intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, WifiP2pDevice::class.java)!!
else -> @Suppress("DEPRECATION") intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)!!
}
Log.e("WFDManager","The device status has changed")
iFaceImpl.onDeviceStatusChanged(thisDevice)
}
}
}
@SuppressLint("MissingPermission")
fun createGroup(){
manager.createGroup(channel, object : ActionListener {
override fun onSuccess() {
Log.e("WFDManager","Successfully created a group with myself as the GO")
}
override fun onFailure(reason: Int) {
Log.e("WFDManager","An error occurred while trying to create a group")
}
})
}
@SuppressLint("MissingPermission")
fun connectToPeer(peer: WifiP2pDevice) {
val config = WifiP2pConfig()
config.deviceAddress = peer.deviceAddress
manager.connect(channel, config, object : ActionListener {
override fun onSuccess() {
Log.e("WFDManager","Successfully attempted to connect to a peer '${peer.deviceName}'")
}
override fun onFailure(reason: Int) {
Log.e("WFDManager","An error occurred while trying to connect to a peer '${peer.deviceName}'")
}
})
}
@SuppressLint("MissingPermission")
fun discoverPeers(){
manager.discoverPeers(channel, object : ActionListener {
override fun onSuccess() {
Log.e("WFDManager","Successfully attempted to discover peers")
}
override fun onFailure(reason: Int) {
Log.e("WFDManager","An error occurred while trying to discover peers")
}
})
}
fun disconnect(){
manager.removeGroup(channel, object : ActionListener {
override fun onSuccess() {
Log.e("WFDManager","Successfully disconnected from the group")
}
override fun onFailure(reason: Int) {
Log.e("WFDManager","An error occurred while trying to disconnect from the group")
}
})
}
}
Inside CommunicationActivity add the following to the onCreate
val manager: WifiP2pManager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
val channel = manager.initialize(this, mainLooper, null)
wfdManager = WifiDirectManager(manager, channel, this)
class CommunicationActivity : AppCompatActivity(), WifiDirectInterface{
Implement the members
override fun onWiFiDirectStateChanged(isEnabled: Boolean) {
var text = "There was a state change in the WiFi Direct. Currently it is "
text = if (isEnabled){
"$text enabled!"
} else {
"$text disabled! Try turning on the WiFi adapter"
}
val toast = Toast.makeText(this, text, Toast.LENGTH_SHORT)
toast.show()
updateUI()
}
override fun onPeerListUpdated(deviceList: Collection<WifiP2pDevice>) {
val toast = Toast.makeText(this, "Updated listing of nearby WiFi Direct devices", Toast.LENGTH_SHORT)
toast.show()
updateUI()
}
override fun onGroupStatusChanged(groupInfo: WifiP2pGroup?) {
val text = if (groupInfo == null){
"Group is not formed"
} else {
"Group has been formed"
}
val toast = Toast.makeText(this, text , Toast.LENGTH_SHORT)
toast.show()
}
override fun onDeviceStatusChanged(thisDevice: WifiP2pDevice) {
val toast = Toast.makeText(this, "Device parameters have been updated" , Toast.LENGTH_SHORT)
toast.show()
}
The logic for the UI for this page is quite complex and follows the following algorithm
IF the WFD adapter is off (disalbed) THEN
SHOW the UI that says we need to turn on the adapter
ELSE IF there is NO WFD connection THEN
SHOW options that would allow the user to either CREATE A GROUP or DISCOVER NEARBY GROUPS
ELSE IF there are nearby groups THEN
SHOW elibigble devices in list
ELSE IF there is a Wfd connection
SHOW chat interface
Let's handle the first scenario. i.e. Displaying an appropriate interface if the adapter is enabled/disabled
The WifiDirectManager class alerts us on whenever our adapter state has changed from enabled to disabled and vice versa. We can use the onWiFiDirectStateChanged method to handle this.
CommunicationActivity called wfdAdapterEnabled. This will store the information on whether the adapter is enabled or disabled. We'd use this in the future to draw an appropriate interfaceprivate var wfdAdapterEnabled = false
add the following line in the onWifiDirectStateChanged method wfdAdapterEnabled = isEnabled
Implement the updateUI call
val wfdAdapterErrorView:ConstraintLayout = findViewById(R.id.clWfdAdapterDisabled)
wfdAdapterErrorView.visibility = if (!wfdAdapterEnabled) View.VISIBLE else View.GONE
The definition of the constraint layout in the activity_communication.xml is as follows
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/clWfdAdapterDisabled"
android:padding="32dp"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/tvWfdError"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="WiFi Direct - Error"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="It seems your WiFi Direct Adapter is off. Please ensure that you turn ON WiFI in your settings!"
android:textSize="11sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvWfdError" />
</androidx.constraintlayout.widget.ConstraintLayout>
Create a new instance variable called wfdHasConnection
Modify the onGroupStatusCHanged implementation. We're going to say that we have a connection if groupInfo is not null wfdHasConnection = groupInfo != null
Modify the updateUI function and add the following
val wfdNoConnectionView:ConstraintLayout = findViewById(R.id.clNoWifiDirectConnection)
wfdNoConnectionView.visibility = if (wfdAdapterEnabled && !wfdHasConnection) View.VISIBLE else View.GONE
The constraint Layout for this will be as follows
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/clNoWifiDirectConnection"
android:padding="32dp"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/tvWfdDisconnected"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="WiFi Direct - Disconnected"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnCreateGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="62dp"
android:layout_marginEnd="50dp"
android:onClick="createGroup"
android:text="Create Group"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintEnd_toStartOf="@+id/btnDiscover"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvWfdDisconnected" />
<Button
android:id="@+id/btnDiscover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="62dp"
android:onClick="discoverNearbyPeers"
android:text="Discover Nearby"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btnCreateGroup"
app:layout_constraintTop_toBottomOf="@+id/tvWfdDisconnected" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvPeerListing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btnCreateGroup" />
</androidx.constraintlayout.widget.ConstraintLayout>
Add a state variable hasDevices
private var hasDevices = false
Update the onPeerListUpdated function
hasDevices = deviceList.isNotEmpty()
Update the updateUI function
val rvPeerList: RecyclerView= findViewById(R.id.rvPeerListing)
rvPeerList.visibility = if (wfdAdapterEnabled && !wfdHasConnection && hasDevices) View.VISIBLE else View.GONE
peerlistPeerListAdapterPeerListAdapterInterfaceImplement the PeerListAdapterInterface
interface PeerListAdapterInterface {
fun onPeerClicked(peer:WifiP2pDevice)
}
Implement the PeerListAdapter
class PeerListAdapter(private val iFaceImpl:PeerListAdapterInterface): RecyclerView.Adapter<PeerListAdapter.ViewHolder>() {
private val peersList:MutableList<WifiP2pDevice> = mutableListOf()
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val titleTextView: TextView = itemView.findViewById(R.id.titleTextView)
val descriptionTextView: TextView = itemView.findViewById(R.id.descriptionTextView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.peer_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val peer = peersList[position]
holder.titleTextView.text = peer.deviceName
holder.descriptionTextView.text = peer.deviceAddress
holder.itemView.setOnClickListener {
iFaceImpl.onPeerClicked(peer)
}
}
override fun getItemCount(): Int {
return peersList.size
}
@SuppressLint("NotifyDataSetChanged")
fun updateList(newPeersList:Collection<WifiP2pDevice>){
peersList.clear()
peersList.addAll(newPeersList)
notifyDataSetChanged()
}
}
Add the adapter as an instance member to the COmmunicationActivity class
private var peerListAdapter:PeerListAdapter? = null
Add the following code at the end of the onCreate
peerListAdapter = PeerListAdapter(this)
val rvPeerList: RecyclerView= findViewById(R.id.rvPeerListing)
rvPeerList.adapter = peerListAdapter
rvPeerList.layoutManager = LinearLayoutManager(this)