Integrasi Mobile•Android (Kotlin)

Implementasi Native Android (Kotlin)

Panduan integrasi lengkap untuk aplikasi **Android Native** menggunakan bahasa Kotlin, komponen android.webkit.WebView, dan WebChromeClient.

1. Konfigurasi AndroidManifest.xml

AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:usesCleartextTraffic="false"
        ... >
        <activity android:name=".QoinWebviewActivity" />
    </application>
</manifest>

2. Activity Lengkap (QoinWebviewActivity.kt)

QoinWebviewActivity.kt
package id.qoin.client

import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.webkit.*
import androidx.appcompat.app.AppCompatActivity
import org.json.JSONObject

class QoinWebviewActivity : AppCompatActivity() {

    private lateinit var webView: WebView
    private var signature: String = ""
    private var palette: String = "original"
    private var baseUrl: String = "https://seamless.qoin.id"

    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_qoin_webview)

        signature = intent.getStringExtra("EXTRA_SIGNATURE") ?: ""
        palette = intent.getStringExtra("EXTRA_PALETTE") ?: "original"
        baseUrl = intent.getStringExtra("EXTRA_BASE_URL") ?: "https://seamless.qoin.id"

        webView = findViewById(R.id.webView)

        val settings = webView.settings
        settings.javaScriptEnabled = true
        settings.domStorageEnabled = true
        settings.setGeolocationEnabled(true)
        settings.databaseEnabled = true

        // JavaScript Bridge untuk menangkap pesan balik
        webView.addJavascriptInterface(object {
            @JavascriptInterface
            fun postMessage(message: String) {
                runOnUiThread {
                    handleWebviewMessage(message)
                }
            }
        }, "QoinBridge")

        // WebChromeClient untuk meneruskan izin Geolocation GPS & Kamera
        webView.webChromeClient = object : WebChromeClient() {
            override fun onGeolocationPermissionsShowPrompt(
                origin: String,
                callback: GeolocationPermissions.Callback
            ) {
                callback.invoke(origin, true, false)
            }

            override fun onPermissionRequest(request: PermissionRequest) {
                if (request.resources.contains(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
                    request.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
                } else {
                    request.deny()
                }
            }
        }

        webView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                injectPostMessage()
            }
        }

        webView.loadUrl("$baseUrl/")
    }

    private fun injectPostMessage() {
        val payload = JSONObject().apply {
            put("Signature", signature)
            put("Palette", palette)
        }.toString()

        val script = """
            (function() {
                window.addEventListener('message', function(event) {
                    if (window.QoinBridge) {
                        var d = typeof event.data === 'string' ? event.data : JSON.stringify(event.data);
                        window.QoinBridge.postMessage(d);
                    }
                });
                window.postMessage($payload, '*');
            })();
        """.trimIndent()

        webView.evaluateJavascript(script, null)

        // Retry pengiriman 500ms kemudian
        Handler(Looper.getMainLooper()).postDelayed({
            webView.evaluateJavascript("window.postMessage($payload, '*');", null)
        }, 500)
    }

    private fun handleWebviewMessage(message: String) {
        try {
            val json = JSONObject(message)
            if (json.optBoolean("close", false)) {
                val intent = Intent().apply {
                    putExtra("RESULT_DATA", message)
                }
                setResult(RESULT_OK, intent)
                finish()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}
Hardware Back Button
Tambahkan penanganan tombol Back perangkat Android agar pengguna dapat kembali ke halaman sebelumnya di dalam WebView (if (webView.canGoBack()) webView.goBack() else finish()).