Wi-Fi Direct (Wi-Fi P2P) হলো একটি টেকনোলজি যা অ্যাক্সেস পয়েন্ট বা ইন্টারনেট ছাড়া দুটি বা ততোধিক ডিভাইসকে সরাসরি Wi-Fi রেডিও ব্যবহার করে কানেক্ট করতে দেয়। এটি ব্লুটুথের চেয়ে অনেক বেশি ব্যান্ডউইথ (upto 250 Mbps) এবং রেঞ্জ দেয়। এই টিউটোরিয়ালে Wi-Fi Direct অন/অফ করার বেসিক ইমপ্লিমেন্টেশন এবং পিয়ার ডিসকভারির কনসেপ্ট দেখানো হলো।
প্রয়োজনীয় পারমিশন
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- API 29+ পিয়ার ডিসকভারির জন্য -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation" /> <!-- API 33+ -->
১. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Wi-Fi Direct: OFF"
android:textSize="18sp"
android:layout_marginBottom="24dp" />
<Button
android:id="@+id/btnToggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Turn On Wi-Fi Direct"
android:paddingHorizontal="24dp"
android:paddingVertical="12dp" />
<Button
android:id="@+id/btnDiscover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Discover Peers"
android:enabled="false"
android:paddingHorizontal="24dp"
android:paddingVertical="12dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvPeers"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="16dp"
android:visibility="gone"
tools:listitem="@layout/item_peer" />
</LinearLayout>
২. item_peer.xml (RecyclerView Item)
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
<TextView
android:id="@+id/tvPeerName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:textSize="16sp"
android:text="Device Name" />
</androidx.cardview.widget.CardView>
৩. WifiDirectBroadcastReceiver.java
package com.example.wifidirectdemo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pManager;
import android.widget.Toast;
import java.util.Collection;
public class WifiDirectBroadcastReceiver extends BroadcastReceiver {
private final WifiP2pManager manager;
private final WifiP2pManager.Channel channel;
private final MainActivity activity;
public WifiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, MainActivity activity) {
this.manager = manager;
this.channel = channel;
this.activity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action == null) return;
switch (action) {
case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
boolean enabled = (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED);
activity.updateWiFiDirectState(enabled);
Toast.makeText(context, enabled ? "Wi-Fi Direct ON" : "Wi-Fi Direct OFF", Toast.LENGTH_SHORT).show();
break;
case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
// পিয়ার লিস্ট আপডেট হয়েছে - রিকোয়েস্ট করুন
if (manager != null && channel != null) {
manager.requestPeers(channel, activity.peerListListener);
}
break;
case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
// কানেকশন স্টেট চেঞ্জড
// NetworkInfo info = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
break;
case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
// এই ডিভাইসের ডিটেইলস চেঞ্জড
// WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
break;
}
}
}
৪. MainActivity.java (Complete Implementation)
package com.example.wifidirectdemo;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private TextView tvStatus;
private Button btnToggle, btnDiscover;
private RecyclerView rvPeers;
private PeerAdapter peerAdapter;
private List peerList = new ArrayList<>();
private WifiP2pManager manager;
private WifiP2pManager.Channel channel;
private BroadcastReceiver receiver;
private IntentFilter intentFilter;
// Peer List Listener
public final WifiP2pManager.PeerListListener peerListListener = new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
peerList.clear();
peerList.addAll(peers.getDeviceList());
peerAdapter.notifyDataSetChanged();
rvPeers.setVisibility(peerList.isEmpty() ? View.GONE : View.VISIBLE);
Toast.makeText(MainActivity.this, peerList.size() + " peers found", Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvStatus = findViewById(R.id.tvStatus);
btnToggle = findViewById(R.id.btnToggle);
btnDiscover = findViewById(R.id.btnDiscover);
rvPeers = findViewById(R.id.rvPeers);
rvPeers.setLayoutManager(new LinearLayoutManager(this));
peerAdapter = new PeerAdapter();
rvPeers.setAdapter(peerAdapter);
// WifiP2pManager ইনিশিয়ালাইজ
manager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);
channel = manager.initialize(this, getMainLooper(), null);
// Broadcast Receiver সেটআপ
receiver = new WifiDirectBroadcastReceiver(manager, channel, this);
intentFilter = new IntentFilter();
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
// Toggle Button
btnToggle.setOnClickListener(v -> toggleWifiDirect());
// Discover Button
btnDiscover.setOnClickListener(v -> discoverPeers());
}
private void toggleWifiDirect() {
// Wi-Fi Direct আলাদা ভাবে ON/OFF করা যায় না - এটি সিস্টেম Wi-Fi-এর সাথে জুড়ে থাকে।
// কিন্তু আমরা WifiP2pManager দিয়ে ডিসকভারি/কানেকশন ম্যানেজ করি।
// এই উদাহরণে শুধু UI টগল করছি।
boolean isEnabled = manager != null; // Simplified
// Real implementation: Wi-Fi ON/OFF via WifiManager (requires CHANGE_WIFI_STATE)
// For Wi-Fi Direct, we just manage discovery/connections via manager.
// Demo: Toggle discovery capability
btnDiscover.setEnabled(!btnDiscover.isEnabled());
btnToggle.setText(btnDiscover.isEnabled() ? "Disable Discovery" : "Enable Discovery");
tvStatus.setText("Discovery: " + (btnDiscover.isEnabled() ? "ENABLED" : "DISABLED"));
}
private void discoverPeers() {
// Runtime Permission Check (API 29+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1001);
return;
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.NEARBY_WIFI_DEVICES)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.NEARBY_WIFI_DEVICES}, 1002);
return;
}
}
if (manager != null && channel != null) {
tvStatus.setText("Discovering peers...");
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
tvStatus.setText("Discovery started...");
}
@Override
public void onFailure(int reasonCode) {
tvStatus.setText("Discovery failed: " + reasonCode);
Toast.makeText(MainActivity.this, "Discovery failed: " + reasonCode, Toast.LENGTH_SHORT).show();
}
});
}
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(receiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if ((requestCode == 1001 || requestCode == 1002) && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
discoverPeers(); // Retry after permission granted
}
}
public void updateWiFiDirectState(boolean enabled) {
runOnUiThread(() -> {
tvStatus.setText("Wi-Fi Direct: " + (enabled ? "ON" : "OFF"));
btnToggle.setText(enabled ? "Disable Wi-Fi Direct" : "Enable Wi-Fi Direct");
btnDiscover.setEnabled(enabled);
});
}
// RecyclerView Adapter
private class PeerAdapter extends RecyclerView.Adapter {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_peer, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
WifiP2pDevice device = peerList.get(position);
holder.tvName.setText(device.deviceName + (device.isGroupOwner() ? " (GO)" : ""));
holder.itemView.setOnClickListener(v -> connectToPeer(device));
}
@Override
public int getItemCount() {
return peerList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView tvName;
ViewHolder(View itemView) {
super(itemView);
tvName = itemView.findViewById(R.id.tvPeerName);
}
}
}
private void connectToPeer(WifiP2pDevice device) {
WifiP2pManager.ConnectionInfoListener connectionListener = info -> {
if (info.groupFormed && info.isGroupOwner) {
// This device is Group Owner (Server)
Toast.makeText(this, "Group Owner - Server Socket Start", Toast.LENGTH_SHORT).show();
} else if (info.groupFormed) {
// This device is Client
Toast.makeText(this, "Client - Connect to " + info.groupOwnerAddress, Toast.LENGTH_SHORT).show();
}
};
WifiP2pManager.WpsInfo wpsInfo = new WifiP2pManager.WpsInfo();
wpsInfo.setup = WifiP2pManager.WPS_PBC; // Push Button Config
manager.connect(channel, new WifiP2pManager.ActionListener() {
@Override public void onSuccess() { Toast.makeText(MainActivity.this, "Connection initiated", Toast.LENGTH_SHORT).show(); }
@Override public void onFailure(int reason) { Toast.makeText(MainActivity.this, "Connect failed: " + reason, Toast.LENGTH_SHORT).show(); }
}, device.deviceAddress, wpsInfo, connectionListener);
}
}
৫. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.wifidirectdemo">
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WiFiDirectDemo">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Wi-Fi Direct কানেকশন ফ্লো (সারসংক্ষেপ)
- Initialization: `WifiP2pManager.initialize()`
- Discovery: `discoverPeers()` → `WIFI_P2P_PEERS_CHANGED_ACTION` → `requestPeers()`
- Connection: `connect()` với `WpsInfo` (PBC/Keypad/Display) → `WIFI_P2P_CONNECTION_CHANGED_ACTION`
- Group Owner Negotiation: এক ডিভাইস GO (Server), অন্যটা Client
- Data Transfer: GO-র IP (`info.groupOwnerAddress`) দিয়ে Socket Communication
গুরুত্বপূর্ণ নোটস
- Wi-Fi Direct ≠ Wi-Fi Hotspot: Hotspot = Infrastructure mode (AP + Clients), Direct = P2P Group Owner + Clients।
- Location Permission: Android 10+ (API 29) পিয়ার ডিসকভারির জন্য `ACCESS_FINE_LOCATION` বাধ্যতামূলক।
- NEARBY_WIFI_DEVICES (API 33+): লোকেশন ছাড়া ওয়াইফাই ডিভাইস ডিসকভারির জন্য।
- Throttling: `discoverPeers()` কল ফ্রিকোয়েন্সি সিস্টেম লিমিট করে (প্রায় ৩০ সেকেন্ড/কল)।
- Legacy WPS: `WPS_PBC` (Push Button) সবচেয়ে সহজ, কিন্তু কিছু ডিভাইস সাপোর্ট করে না।
পরবর্তী ধাপ: ডেটা ট্রান্সফার
কানেকশন স্থাপন হলে (Group Formed), আপনি নিচের মতো সকেট ব্যবহার করে ডেটা ট্রান্সফার করতে পারেন:
// Server (Group Owner) - Thread-এ ServerSocket serverSocket = new ServerSocket(8888); Socket client = serverSocket.accept(); InputStream in = client.getInputStream(); // Read data... // Client - Thread-এ Socket socket = new Socket(); socket.connect(new InetSocketAddress(groupOwnerAddress, 8888), 5000); OutputStream out = socket.getOutputStream(); // Write data...
Wi-Fi Direct হাই-ব্যান্ডউইথ, লো-ল্যাটেন্সি পিয়ার-টু-পিয়ার কমিউনিকেশনের জন্য আদর্শ। ফাইল শেয়ারিং, মাল্টিপ্লেয়ার গেমিং, স্ক্রিন কাস্টিং, IoT ডিভাইস কন্ট্রোল ইত্যাদিতে এটি ব্যাপকভাবে ব্যবহৃত হয়।