package com.topon import android.app.Activity import android.content.Context import android.view.ViewGroup import android.widget.FrameLayout import com.facebook.react.bridge.Arguments import com.secmtp.sdk.core.api.ATAdInfo import com.secmtp.sdk.core.api.ATAdStatusInfo import com.secmtp.sdk.core.api.ATNetworkConfirmInfo import com.secmtp.sdk.core.api.ATShowConfig import com.secmtp.sdk.core.api.AdError import com.secmtp.sdk.splashad.api.ATSplashAd import com.secmtp.sdk.splashad.api.ATSplashAdExtraInfo import com.secmtp.sdk.splashad.api.ATSplashExListener import org.json.JSONObject class SplashHelper(module: ToponModule) : BaseHelper(module) { private var splashAd: ATSplashAd? = null private var placementId: String = "" private var isReady: Boolean = false private var adContainer: FrameLayout? = null private var decorView: ViewGroup? = null private fun ensureSplash(initPlacementId: String, fetchTimeoutMs: Int) { if (splashAd != null && placementId == initPlacementId) { return } placementId = initPlacementId val activity = currentActivity() if (activity == null) { MsgTools.printMsg("initSplash failed, activity is null: $placementId") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putString(Const.CallbackKey.ErrorMsg, "activity is null") sendEvent(Const.SplashCallback.LoadFailCallbackKey, map) return } val listener = object : ATSplashExListener { override fun onAdLoaded(isTimeout: Boolean) { isReady = true MsgTools.printMsg("splash onAdLoaded: $placementId, isTimeout: $isTimeout") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putBoolean(Const.CallbackKey.IsTimeout, isTimeout) sendEvent(Const.SplashCallback.LoadedCallbackKey, map) } override fun onAdLoadTimeout() { isReady = false MsgTools.printMsg("splash onAdLoadTimeout: $placementId") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) sendEvent(Const.SplashCallback.TimeoutCallbackKey, map) } override fun onNoAdError(adError: AdError) { isReady = false MsgTools.printMsg("splash onNoAdError: $placementId, ${adError.fullErrorInfo}") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putString(Const.CallbackKey.ErrorMsg, adError.fullErrorInfo) sendEvent(Const.SplashCallback.LoadFailCallbackKey, map) } override fun onAdShow(adInfo: ATAdInfo) { MsgTools.printMsg("splash onAdShow: $placementId") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putString(Const.CallbackKey.AdInfo, adInfo.toString()) sendEvent(Const.SplashCallback.ShowCallbackKey, map) } override fun onAdClick(adInfo: ATAdInfo) { MsgTools.printMsg("splash onAdClick: $placementId") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putString(Const.CallbackKey.AdInfo, adInfo.toString()) sendEvent(Const.SplashCallback.ClickCallbackKey, map) } override fun onAdDismiss(adInfo: ATAdInfo, extraInfo: ATSplashAdExtraInfo) { MsgTools.printMsg("splash onAdDismiss: $placementId") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putString(Const.CallbackKey.AdInfo, adInfo.toString()) sendEvent(Const.SplashCallback.CloseCallbackKey, map) cleanupContainer() } override fun onDeeplinkCallback(adInfo: ATAdInfo, isSuccess: Boolean) { MsgTools.printMsg("splash onDeeplinkCallback: $placementId, success: $isSuccess") } override fun onDownloadConfirm( context: Context, adInfo: ATAdInfo, confirmInfo: ATNetworkConfirmInfo ) { val handled = tryHandleDownloadConfirm(context, adInfo, confirmInfo) MsgTools.printMsg("splash onDownloadConfirm: $placementId, handled: $handled") } } splashAd = if (fetchTimeoutMs > 0) { ATSplashAd(activity, placementId, listener, fetchTimeoutMs) } else { ATSplashAd(activity, placementId, listener) } } fun loadSplash(placementId: String, settingsJson: String) { MsgTools.printMsg("loadSplash: $placementId, settings: $settingsJson") runOnUiThread { var fetchTimeout = -1 if (settingsJson.isNotEmpty()) { try { val jsonObject = JSONObject(settingsJson) fetchTimeout = jsonObject.optInt("tolerateTimeout", -1) } catch (t: Throwable) { MsgTools.printMsg("loadSplash parse settings failed: ${t.message}") } } ensureSplash(placementId, fetchTimeout) val ad = splashAd ?: return@runOnUiThread val localExtra = CommonUtil.jsonStringToMap(settingsJson).toMutableMap() ad.setLocalExtra(localExtra) ad.loadAd() } } fun showSplash(scenario: String) { val safeScenario = scenario.take(1024) MsgTools.printMsg("showSplash: $placementId, scenario: $safeScenario") runOnUiThread { val ad = splashAd val activity = currentActivity() if (ad == null || activity == null) { MsgTools.printMsg("showSplash failed: activity or ad is null $placementId") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putString(Const.CallbackKey.ErrorMsg, "activity or ad is null") sendEvent(Const.SplashCallback.LoadFailCallbackKey, map) return@runOnUiThread } if (!ad.isAdReady) { MsgTools.printMsg("showSplash failed: ad is not ready $placementId") val map = Arguments.createMap() map.putString(Const.CallbackKey.PlacementId, placementId) map.putString(Const.CallbackKey.ErrorMsg, "ad is not ready") sendEvent(Const.SplashCallback.LoadFailCallbackKey, map) return@runOnUiThread } if (safeScenario.isNotEmpty()) { ATSplashAd.entryAdScenario(placementId, safeScenario) } val container = ensureContainer(activity) val showConfig = ATShowConfig.Builder() .scenarioId(safeScenario) .build() ad.show(activity, container, null, showConfig) } } fun isAdReady(): Boolean { MsgTools.printMsg("splash isAdReady: $placementId") return try { splashAd?.isAdReady ?: isReady } catch (t: Throwable) { MsgTools.printMsg("splash isAdReady Throwable: ${t.message}") isReady } } fun checkAdStatus(): String { MsgTools.printMsg("splash checkAdStatus: $placementId") return try { val statusInfo: ATAdStatusInfo? = splashAd?.checkAdStatus() if (statusInfo != null) { val jsonObject = JSONObject() jsonObject.put("isLoading", statusInfo.isLoading) jsonObject.put("isReady", statusInfo.isReady) jsonObject.put("adInfo", statusInfo.getATTopAdInfo()) val result = jsonObject.toString() MsgTools.printMsg("splash checkAdStatus: $placementId, $result") result } else { MsgTools.printMsg("splash checkAdStatus error, you must call loadSplash first $placementId") "" } } catch (t: Throwable) { MsgTools.printMsg("splash checkAdStatus Throwable: ${t.message}") "" } } private fun ensureContainer(activity: Activity): ViewGroup { if (decorView == null) { decorView = (activity.window?.decorView as? ViewGroup) ?: activity.findViewById(android.R.id.content) } if (adContainer == null) { adContainer = FrameLayout(activity) } val container = adContainer ?: FrameLayout(activity) val parent = container.parent if (parent is ViewGroup) { parent.removeView(container) } val params = FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) container.layoutParams = params container.fitsSystemWindows = false decorView?.addView(container, params) return container } private fun cleanupContainer() { val container = adContainer ?: return val parent = container.parent if (parent is ViewGroup) { container.removeAllViews() parent.removeView(container) } } private fun tryHandleDownloadConfirm( context: Context, adInfo: ATAdInfo, confirmInfo: ATNetworkConfirmInfo ): Boolean { val cls = confirmInfo.javaClass val attempts = listOf( { cls.methods.firstOrNull { it.name == "showConfirmDialog" && it.parameterTypes.contentEquals(arrayOf(Context::class.java, ATAdInfo::class.java)) }?.invoke(confirmInfo, context, adInfo) != null }, { cls.methods.firstOrNull { it.name == "showConfirmDialog" && it.parameterTypes.contentEquals(arrayOf(Context::class.java)) }?.invoke(confirmInfo, context) != null }, { val methodNames = listOf("confirm", "onConfirm", "clickConfirm", "proceed") methodNames.any { name -> cls.methods.firstOrNull { it.name == name && it.parameterTypes.isEmpty() } ?.invoke(confirmInfo) != null } } ) for (attempt in attempts) { try { if (attempt()) { return true } } catch (_: Throwable) { // ignore and try next fallback } } return false } }