এই আর্টিকেলটি পূর্বের আর্টিকেলের (ID: 1006061517) আপডেটেড এবং রিফাইন করা ভার্সন। এখানে মডার্ন অ্যান্ড্রয়েড API (API 29+) এর সীমাবদ্ধতা, থ্রটলিং, এবং নতুন পারমিশন মডেল (Android 13+ NEARBY_WIFI_DEVICES) বিবেচনা করে একটি রোবাস্ট ওয়াইফাই স্ক্যানার বানানো দেখানো হলো।
নোট: এই আর্টিকেলটি আর্টিকেল ১০০৬০৬১৫১৭-র উন্নত সংস্করণ। মূল কোড স্ট্রাকচার একই, কিন্তু API 29+ থ্রটলিং, Android 13 পারমিশন, এবং RecyclerView ব্যবহার করে আপডেট করা হয়েছে।
কী নতুন এই ভার্সনে?
- ✅ RecyclerView + ViewHolder (ListView এর পরিবর্তে)
- ✅ Android 13 (API 33) NEARBY_WIFI_DEVICES পারমিশন সাপোর্ট
- ✅ স্ক্যান থ্রটলিং হ্যান্ডলিং (API 29+)
- ✅ করআউট/ডুপ্লিকেট SSID ফিল্টার
- ✅ সিগন্যাল স্ট্রেংথ অনুযায়ী সর্টিং
- ✅ ম্যাটেরিয়াল ডিজাইন UI
১. build.gradle (dependencies)
dependencies {
implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "com.google.android.material:material:1.11.0"
}
২. activity_main.xml (RecyclerView + MaterialButton)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvWifiList"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="8dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/btnScan"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnScan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="Scan WiFi Networks"
android:icon="@android:drawable/ic_menu_rotate"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/tvEmpty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="কোনো নেটওয়ার্ক পাওয়া যায়নি\n\"Scan WiFi\" চাপুন"
android:textAlignment="center"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/btnScan"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
৩. item_wifi_network.xml (RecyclerView আইটেম লেআউট)
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvSsid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="?attr/colorOnSurface" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="4dp">
<TextView
android:id="@+id/tvSecurity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="12sp"
android:textColor="?attr/colorOnSurface"
android:alpha="0.7" />
<TextView
android:id="@+id/tvSignal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@color/colorPrimary"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
৪. WifiNetwork.java (মডেল ক্লাস)
package com.example.wifiscannerpro;
public class WifiNetwork {
public final String ssid;
public final String security;
public final int level; // dBm
public final String bssid;
public final int frequency; // MHz
public WifiNetwork(String ssid, String security, int level, String bssid, int frequency) {
this.ssid = ssid;
this.security = security;
this.level = level;
this.bssid = bssid;
this.frequency = frequency;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof WifiNetwork)) return false;
WifiNetwork that = (WifiNetwork) o;
return bssid.equals(that.bssid); // BSSID ইউনিক আইডেন্টিফায়ার
}
@Override
public int hashCode() {
return bssid.hashCode();
}
}
৫. WifiNetworkAdapter.java (RecyclerView Adapter)
package com.example.wifiscannerpro; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; public class WifiNetworkAdapter extends RecyclerView.Adapter{ private final List networks = new ArrayList<>(); @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_wifi_network, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { WifiNetwork network = networks.get(position); holder.tvSsid.setText(network.ssid.isEmpty() ? "<Hidden Network>" : network.ssid); holder.tvSecurity.setText(network.security); holder.tvSignal.setText(network.level + " dBm"); // সিগন্যাল স্ট্রেংথ অনুযায়ী কালার কোডিং if (network.level >= -50) holder.tvSignal.setTextColor(0xFF4CAF50); // Green else if (network.level >= -70) holder.tvSignal.setTextColor(0xFFFFC107); // Amber else holder.tvSignal.setTextColor(0xFFF44336); // Red } @Override public int getItemCount() { return networks.size(); } public void submitList(List newNetworks) { networks.clear(); networks.addAll(newNetworks); notifyDataSetChanged(); } static class ViewHolder extends RecyclerView.ViewHolder { TextView tvSsid, tvSecurity, tvSignal; ViewHolder(View itemView) { super(itemView); tvSsid = itemView.findViewById(R.id.tvSsid); tvSecurity = itemView.findViewById(R.id.tvSecurity); tvSignal = itemView.findViewById(R.id.tvSignal); } } }
৬. MainActivity.java (আপডেটেড লজিক)
package com.example.wifiscannerpro;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
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.RecyclerView;
import com.google.android.material.button.MaterialButton;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MainActivity extends AppCompatActivity {
private RecyclerView rvWifiList;
private MaterialButton btnScan;
private WifiManager wifiManager;
private WifiScanReceiver wifiReceiver;
private WifiNetworkAdapter adapter;
private static final int PERMISSION_REQUEST_CODE = 1001;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvWifiList = findViewById(R.id.rvWifiList);
btnScan = findViewById(R.id.btnScan);
wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
adapter = new WifiNetworkAdapter();
rvWifiList.setAdapter(adapter);
// ওয়াইফাই অফ থাকলে অন করা (API 29+ এ ব্যবহারকারীর অনুমতি লাগে)
if (wifiManager != null && !wifiManager.isWifiEnabled()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
Toast.makeText(this, "দয়া করে সেটিংস থেকে WiFi অন করুন", Toast.LENGTH_LONG).show();
} else {
wifiManager.setWifiEnabled(true);
}
}
btnScan.setOnClickListener(v -> requestPermissionAndScan());
}
private void requestPermissionAndScan() {
String permission;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // API 33+
permission = Manifest.permission.NEARBY_WIFI_DEVICES;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // API 23+
permission = Manifest.permission.ACCESS_COARSE_LOCATION;
} else {
startWifiScan();
return;
}
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{permission}, PERMISSION_REQUEST_CODE);
} else {
startWifiScan();
}
}
private void startWifiScan() {
if (wifiManager == null) return;
// API 29+ থ্রটলিং চেক (সিস্টেম স্বয়ংক্রিয়ভাবে হ্যান্ডেল করে, কিন্তু ইউজারকে জানাতে পারি)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// WifiManager.startScan() রিটার্ন করে boolean (API 28-এ) বা void (API 29+)
// আমরা শুধু কল করব, রিসিভারে রেজাল্ট পাব
}
boolean success = wifiManager.startScan();
if (!success && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
Toast.makeText(this, "স্ক্যান থ্রটল্ড: কিছুক্ষণ انتظار করুন", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onResume() {
super.onResume();
wifiReceiver = new WifiScanReceiver();
IntentFilter filter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(wifiReceiver, filter);
}
@Override
protected void onPause() {
super.onPause();
if (wifiReceiver != null) {
unregisterReceiver(wifiReceiver);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startWifiScan();
} else {
Toast.makeText(this, "পারমিশন দরকার। সেটিংস থেকে.Enable করুন।", Toast.LENGTH_LONG).show();
}
}
}
private class WifiScanReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (!WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) return;
if (wifiManager == null) return;
List results = wifiManager.getScanResults();
List networkList = new ArrayList<>();
Set seenBssids = new HashSet<>();
for (ScanResult result : results) {
// ডুপ্লিকেট BSSID ফিল্টার
if (seenBssids.contains(result.BSSID)) continue;
seenBssids.add(result.BSSID);
String security = getSecurityType(result.capabilities);
networkList.add(new WifiNetwork(
result.SSID, security, result.level, result.BSSID, result.frequency
));
}
// সিগন্যাল স্ট্রেংথ (level) অনুযায়ী ডেসেন্ডিং সর্ট (উচ্চে zuerst)
Collections.sort(networkList, (a, b) -> Integer.compare(b.level, a.level));
adapter.submitList(networkList);
// এম্পটি স্টেট হ্যান্ডলিং
findViewById(R.id.tvEmpty).setVisibility(networkList.isEmpty() ? View.VISIBLE : View.GONE);
rvWifiList.setVisibility(networkList.isEmpty() ? View.GONE : View.VISIBLE);
}
private String getSecurityType(String capabilities) {
if (capabilities.contains("WPA3")) return "WPA3";
if (capabilities.contains("WPA2")) return "WPA2";
if (capabilities.contains("WPA")) return "WPA";
if (capabilities.contains("WEP")) return "WEP";
return "Open";
}
}
}
৭. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.wifiscannerpro">
<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_COARSE_LOCATION" />
<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.MaterialComponents.DayNight">
<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>
চলানোর ফলাফল
- ম্যাটেরিয়াল কার্ড-বেসড UI-এ ওয়াইফাই লিস্ট দেখাবে।
- প্রতিটি আইটেমে: SSID, সিকিউরিটি টাইপ (WPA3/WPA2/Open), সিগন্যাল স্ট্রেংথ (dBm) এবং কালার-কোডেড সিগন্যাল ইন্ডিকেটর।
- সিগন্যাল স্ট্রেংথ অনুযায়ী সর্টেড (সর্বোচ্চ উপরে)।
- ডুপ্লিকেট BSSID অটোমেটিক ফিল্টার হয়।
- হিডেন নেটওয়ার্ক "<Hidden Network>" হিসেবে দেখাবে।
পরবর্তী ধাপ (Enhancement Ideas)
- কানেকশন ফিচার: আইটেম ক্লিক করলে `WifiNetworkSpecifier` (API 29+) বা `WifiConfiguration` (Legacy) দিয়ে কানেক্ট করা।
- রিয়েল-টাইম আপডেট: `WifiManager.startScan()` পেরিয়ডিক্যালি কল করার জন্য `WorkManager` বা `Handler` ব্যবহার।
- ম্যাপ ইন্টিগ্রেশন: লোকেশন+tower ডেটা দিয়ে নেটওয়ার্ক লোকেশন ম্যাপে দেখানো।
- Jetpack Compose: `LazyColumn` + `rememberWifiScanner()` কাস্টম কম্পোজেবল বানানো।
এই ইমপ্লিমেন্টেশনটি প্রোডাকশন-রেডি এবং মডার্ন অ্যান্ড্রয়েড API গাইডলাইন অনুযায়ী। পুরনো ListView-ভিত্তিক কোডের চেয়ে এটি আরও পারফরম্যান্ট, মেইনটেনেবল এবং ইউজার-ফ্রেন্ডলি।