কম্পিউটার টিউটোরিয়াল

অ্যান্ড্রয়েডে Wi-Fi Direct ব্যবহার করার পদ্ধতি

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 কানেকশন ফ্লো (সারসংক্ষেপ)

  1. Initialization: `WifiP2pManager.initialize()`
  2. Discovery: `discoverPeers()` → `WIFI_P2P_PEERS_CHANGED_ACTION` → `requestPeers()`
  3. Connection: `connect()` với `WpsInfo` (PBC/Keypad/Display) → `WIFI_P2P_CONNECTION_CHANGED_ACTION`
  4. Group Owner Negotiation: এক ডিভাইস GO (Server), অন্যটা Client
  5. 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 ডিভাইস কন্ট্রোল ইত্যাদিতে এটি ব্যাপকভাবে ব্যবহৃত হয়।

  1. এন্ড্রয়েডে ViewFlipper ব্যবহার করার পদ্ধতি

  2. অ্যান্ড্রয়েডে Snackbar ব্যবহার করার পদ্ধতি

  3. Android-এ NavigationView ব্যবহার করে নেভিগেশন ড্রয়ার তৈরির পদ্ধতি

  4. আপনার ফোনকে WiFi হটস্পট হিসেবে ব্যবহার করার পদ্ধতি