package com.complycubereactnative import android.app.Activity import android.content.Intent import com.facebook.react.bridge.* import org.json.JSONObject import android.util.Log private const val REQUEST_CODE_COMPLYCUBE = 10010 private const val ERROR_ACTIVITY = "ACTIVITY_ERROR" private const val ERROR_SESSION = "SESSION_ERROR" class ComplyCubeModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), ActivityEventListener { companion object { @Volatile var lastReactContext: ReactApplicationContext? = null } private var pendingPromise: Promise? = null init { lastReactContext = reactContext reactContext.addActivityEventListener(this) } // Expose the JS name "ComplyCube" override fun getName(): String = "ComplyCubeModule" @ReactMethod fun start(settingsMap: ReadableMap, promise: Promise) { lastReactContext = reactContext val activity = reactContext.getCurrentActivity() if (activity == null) { promise.reject(ERROR_ACTIVITY, "No activity available") return } if (pendingPromise != null) { promise.reject(ERROR_SESSION, "A session is already in progress") return } pendingPromise = promise // Convert to REAL JSON (not map.toString()) val jsonSettings = JSONObject(settingsMap.toHashMap() as Map<*, *>).toString() val intent = Intent(activity, ComplyCubeActivity::class.java).apply { putExtra("EXTRA_SETTINGS_JSON", jsonSettings) } @Suppress("DEPRECATION") activity.startActivityForResult(intent, REQUEST_CODE_COMPLYCUBE) } override fun onActivityResult( activity: Activity, requestCode: Int, resultCode: Int, data: Intent? ) { if (requestCode != REQUEST_CODE_COMPLYCUBE) return val promise = pendingPromise pendingPromise = null if (promise == null) return val resultStr = data?.getStringExtra("result") if (resultStr.isNullOrBlank()) { if (resultCode == Activity.RESULT_OK) { promise.resolve(null) } else { promise.reject(ERROR_SESSION, "No result from activity") } return } try { val obj = org.json.JSONObject(resultStr) val type = obj.optString("type", "") if (resultCode == Activity.RESULT_OK && (type.isBlank() || type == "SUCCESS")) { val payload = obj.opt("payload") if (payload != null) { promise.resolve(payload.toString()) } else { promise.resolve(resultStr) } } else { val msg = obj.optString("message", "An unexpected error occurred.") val code = when (type) { "CANCELED" -> "E_CANCELLED" "BAD_CONFIG" -> "E_BAD_CONFIG" "ERROR" -> "E_SDK" else -> "E_SDK" } promise.reject(code, msg) } } catch (_: Throwable) { if (resultCode == Activity.RESULT_OK) { promise.resolve(resultStr) } else { promise.reject(ERROR_SESSION, resultStr) } } } override fun onNewIntent(intent: Intent) { /* no-op */ } @Deprecated("React Native base API deprecates this lifecycle callback") @Suppress("DEPRECATION") override fun onCatalystInstanceDestroy() { if (lastReactContext === reactContext) { lastReactContext = null } reactContext.removeActivityEventListener(this) pendingPromise?.reject(ERROR_SESSION, "Bridge destroyed before session completed") pendingPromise = null super.onCatalystInstanceDestroy() } } // -------- Helpers (unchanged) -------- fun ReadableMap.toHashMap(): HashMap { val map = HashMap() val iter = keySetIterator() while (iter.hasNextKey()) { val key = iter.nextKey() when (getType(key)) { ReadableType.Null -> map[key] = "null" ReadableType.Boolean -> map[key] = getBoolean(key) ReadableType.Number -> map[key] = getDouble(key) ReadableType.String -> map[key] = getString(key)!! ReadableType.Map -> map[key] = getMap(key)!!.toHashMap() ReadableType.Array -> map[key] = getArray(key)!!.toArrayList() } } return map } fun ReadableArray.toArrayList(): ArrayList { val list = ArrayList(size()) for (i in 0 until size()) { when (getType(i)) { ReadableType.Null -> list.add("null") ReadableType.Boolean -> list.add(getBoolean(i)) ReadableType.Number -> list.add(getDouble(i)) ReadableType.String -> list.add(getString(i)!!) ReadableType.Map -> list.add(getMap(i)!!.toHashMap()) ReadableType.Array -> list.add(getArray(i)!!.toArrayList()) } } return list }