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

Android WebView-এ HTML বাটন ক্লিক জাভাস্ক্রিপ্ট দিয়ে ডিটেক্ট করার পদ্ধতি

WebView-এ JavaScript Interface দিয়ে HTML বাটন হ্যান্ডলিং

এই টিউটোরিয়ালে দেখানো হয়েছে কীভাবে Android WebView-এ লোড করা HTML পেজের বাটন ক্লিক ইভেন্ট নেটিভ Android কোডে (Java/Kotlin-এ) রিসিভ করা যায়। এটি @JavascriptInterface এবং addJavascriptInterface() ব্যবহার করে করা হয়।

সুরক্ষা সতর্কতা ⚠️

@JavascriptInterface অ্যানোটেশন যুক্ত মেথডهاয় যেকোনো পাবলিক মেথড JavaScript থেকে কল করা যায়। শুধু বিশ্বস্ত কন্টেন্ট (লোকাল asset, নিজের সার্ভার) লোড করুন। থার্ড-পার্টি ওয়েবসাইট লোড করলে সিকিউরিটি রিস্ক থাকে।

ধাপ ১: প্রজেক্ট সেটআপ

Package: com.example.webviewjsbridge

ধাপ ২: লেআউট (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnLoad"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:onClick="loadWebPage"
        android:text="Web Page লোড করুন" />

</RelativeLayout>

ধাপ ৩: HTML ফাইল (assets/page.html)

src/main/assets/page.html ফাইল তৈরি করুন (assets ফোল্ডার না থাকলে New → Folder → Assets Folder):

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebView JS Bridge Demo</title>
    <style>
        body { font-family: sans-serif; padding: 20px; }
        .input-group { margin-bottom: 16px; }
        label { display: block; margin-bottom: 4px; font-weight: bold; }
        input { width: 100%; padding: 12px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; }
        button { background: #4CAF50; color: white; border: none; padding: 12px 24px; border-radius: 4px; font-size: 16px; cursor: pointer; width: 100%; }
        button:active { background: #45a049; }
        #result { margin-top: 16px; padding: 12px; background: #f5f5f5; border-radius: 4px; display: none; }
    </style>
</head>
<body>
    <h2>JavaScript Bridge Demo</h2>
    
    <div class="input-group">
        <label>First Name:</label>
        <input type="text" id="txtFirstName" placeholder="Enter first name">
    </div>
    
    <div class="input-group">
        <label>Last Name:</label>
        <input type="text" id="txtLastName" placeholder="Enter last name">
    </div>
    
    <button type="button" id="btnSubmit" onclick="sendToAndroid()">Android-এ পাঠান</button>
    
    <div id="result"></div>

    <script>
        function sendToAndroid() {
            var firstName = document.getElementById('txtFirstName').value.trim();
            var lastName = document.getElementById('txtLastName').value.trim();
            
            if (!firstName || !lastName) {
                alert('দয়া করে উভয় ফিল্ড পূরণ করুন');
                return;
            }
            
            var fullName = firstName + ' ' + lastName;
            
            // Android Interface কল
            if (typeof AndroidBridge !== 'undefined' && AndroidBridge.onButtonClick) {
                AndroidBridge.onButtonClick(fullName);
            } else {
                document.getElementById('result').style.display = 'block';
                document.getElementById('result').innerText = 'Android Bridge পাওয়া যায়নি। অ্যাপে ইনজেক্ট করা হয়নি।';
            }
        }
        
        // Android থেকে কলব্যাক রিসিভ করার ফাংশন
        function onAndroidResponse(message) {
            document.getElementById('result').style.display = 'block';
            document.getElementById('result').innerText = 'Android থেকে প্রতিক্রিয়া: ' + message;
        }
    </script>
</body>
</html>

ধাপ ৪: JavaScript Interface ক্লাস

package com.example.webviewjsbridge;

import android.webkit.JavascriptInterface;
import android.widget.Toast;

public class WebAppInterface {
    private final MainActivity activity;

    public WebAppInterface(MainActivity activity) {
        this.activity = activity;
    }

    @JavascriptInterface
    public void onButtonClick(String fullName) {
        // এই মেথড JavaScript থেকে কল হবে
        // UI thread-এ রান করানোর জন্য runOnUiThread ব্যবহার করুন
        activity.runOnUiThread(() -> {
            Toast.makeText(activity, "JavaScript থেকে পাওয়া: " + fullName, Toast.LENGTH_LONG).show();
            
            // অপশনাল: JavaScript-এ ফিরিয়ে পাঠান
            activity.sendResponseToJs("নাম রিসিভ করা হয়েছে: " + fullName);
        });
    }
}

ধাপ ৫: MainActivity.java

package com.example.webviewjsbridge;

import android.os.Bundle;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void loadWebPage(View view) {
        webView = new WebView(this);
        setContentView(webView);

        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true); // জাভাস্ক্রিপ্ট সক্ষম করুন
        settings.setDomStorageEnabled(true);
        settings.setAllowFileAccess(true);
        settings.setAllowContentAccess(true);

        // জাভাস্ক্রিপ্ট ইন্টারফেস যোগ করুন
        webView.addJavascriptInterface(new WebAppInterface(this), "AndroidBridge");

        // WebViewClient সেট করুন (লিঙ্ক ক্লিকে ব্রাউজার খুলবে না)
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                Toast.makeText(MainActivity.this, "পেজ লোড সম্পন্ন", Toast.LENGTH_SHORT).show();
            }
        });

        // Assets ফোল্ডার থেকে HTML লোড
        webView.loadUrl("file:///android_asset/page.html");
    }

    // JavaScript-এ ফিরিয়ে পাঠানোর মেথড
    public void sendResponseToJs(String message) {
        if (webView != null) {
            // JavaScript ফাংশন কল: onAndroidResponse(message)
            // escapeSingleQuotes দিয়ে স্ট্রিং স্যানিটাইজ করুন
            String escaped = message.replace("'", "\\'").replace("\n", "\\n").replace("\r", "");
            webView.evaluateJavascript("javascript:onAndroidResponse('" + escaped + "')", null);
        }
    }

    @Override
    public void onBackPressed() {
        if (webView != null && webView.canGoBack()) {
            webView.goBack();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    protected void onDestroy() {
        if (webView != null) {
            webView.removeJavascriptInterface("AndroidBridge");
            webView.destroy();
        }
        super.onDestroy();
    }
}

ধাপ ৬: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.webviewjsbridge">

    <uses-permission android:name="android.permission.INTERNET" />

    <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.WebViewJSBridge">

    <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>

গুরুত্বপূর্ণ সিকিউরিটি বেস্ট প্র্যাকটিস

নীতিমালাকারণ
শুধু @JavascriptInterface অ্যানোটেশনযুক্ত মেথড এক্সপোজ করুনAPI 17+ এ অন্য মেথডها অ্যাক্সেসযোগ্য নয়, কিন্তু সাবধান থাকুন
শুধু লোকাল (assets) বা HTTPS ওয়েবসাইট লোড করুনHTTP বা অজানা সাইটে XSS আটাক সম্ভব
Input validation করুনJavaScript থেকে আসা ডাটা স্যানিটাইজ করুন
ProGuard/R8-এ interface ক্লাস keep করুনঅন্যথায় রিলিজ বিল্ডে মেথড রিমুভ হয়ে যাবে
onDestroy-এ removeJavascriptInterface কল করুনমেমরি লিক এবং সিকিউরিটি হোল প্রতিরোধ

ProGuard/R8 Rules (proguard-rules.pro)

-keepclassmembers class com.example.webviewjsbridge.WebAppInterface {
    public *;
}
-keep class com.example.webviewjsbridge.WebAppInterface { *; }

উন্নত ফিচার: দ্বিমুখী যোগাযোগ

// JavaScript-এ (page.html)
function onNativeEvent(eventName, data) {
    console.log('Native event:', eventName, data);
    // UI আপডেট করুন
}

// Android-এ (MainActivity)
public void sendEventToJs(String eventName, String data) {
    if (webView != null) {
        String js = String.format("javascript:onNativeEvent('%s', '%s')", 
                eventName.replace("'", "\\'"), data.replace("'", "\\'"));
        webView.evaluateJavascript(js, null);
    }
}

পরীক্ষা পদ্ধতি

  1. অ্যাপ চালু করুন → "Web Page লোড করুন" চাপুন
  2. First Name এবং Last Name ফিল্ড পূরণ করুন
  3. "Android-এ পাঠান" বাটন চাপুন
  4. টোস্ট মেসেজ দেখুন: "JavaScript থেকে পাওয়া: [নাম]"
  5. WebView-এ "Android থেকে প্রতিক্রিয়া: নাম রিসিভ করা হয়েছে: [নাম]" দেখুন

সাধারণ সমস্যা ও সমাধান

সমস্যাসমাধান
JavaScript Interface কল হয় নাaddJavascriptInterface() কল হয়েছে কিনা, নাম "AndroidBridge" মিলছে কিনা চেক করুন
Release build-এ কাজ করে নাProGuard/R8 rules যোগ করুন interface ক্লাস keep করার জন্য
evaluateJavascript কাজ করে নাAPI 19+ দরকার, WebViewClient সেট আছে কিনা চেক করুন
Security Exceptionশুধু trusted origin (file:///android_asset/ বা HTTPS) লোড করুন
  1. অ্যান্ড্রয়েড ListView-এ ক্লিক ইভেন্ট হ্যান্ডল করার পদ্ধতি

  2. এন্ড্রয়েডে প্রোগ্রামেটিক্যালি ক্যামেরা ক্লিক করার পদ্ধতি

  3. অ্যান্ড্রয়েডে HTML পার্স করার পদ্ধতি (Jsoup লাইব্রেরি ব্যবহার করে)

  4. অ্যান্ড্রয়েডে ডিভাইস ফোন নাকি ট্যাবলেট সেটা ডিটেক্ট করা