From ae54e59a5e9c2a66ce2a86d2f79e81b394d23336 Mon Sep 17 00:00:00 2001 From: Kuntimaddi Manideep Date: Thu, 12 Mar 2026 11:38:19 +0530 Subject: [PATCH] feat: update Airborne integration and logging implementation --- .../react/ReactNativeController.kt | 14 ++-- app/src/main/res/values/strings.xml | 4 +- hyperswitch-sdk-android-airborne/build.gradle | 2 +- .../{HyperOtaLogger.kt => AirborneLogger.kt} | 76 +++++++++---------- .../io/hyperswitch/airborne/AirborneOTA.kt | 72 ++++++++---------- .../kotlin/io/hyperswitch/logs/LogData.kt | 4 +- 6 files changed, 83 insertions(+), 89 deletions(-) rename hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/{HyperOtaLogger.kt => AirborneLogger.kt} (65%) diff --git a/app/src/main/kotlin/io/hyperswitch/react/ReactNativeController.kt b/app/src/main/kotlin/io/hyperswitch/react/ReactNativeController.kt index cc32751e..3b401cbd 100644 --- a/app/src/main/kotlin/io/hyperswitch/react/ReactNativeController.kt +++ b/app/src/main/kotlin/io/hyperswitch/react/ReactNativeController.kt @@ -41,14 +41,14 @@ object ReactNativeController { private var isInitialized = false /** - * Resolves the JavaScript bundle path using Hyper Airborne OTA if available. + * Resolves the JavaScript bundle path using Airborne OTA if available. * * Behavior: * - Determines SDK environment using the publishable key - * - Reads OTA endpoint from resources based on environment + * - Reads Airborne release config endpoint from resources based on environment * - Dynamically loads AirborneOTA via reflection (optional dependency) * - Fetches the OTA-downloaded bundle path - * - Falls back to bundled assets if OTA is disabled, unavailable, or fails + * - Falls back to bundled assets if Airborne is disabled, unavailable, or fails * * @param application Application context * @return Path to the JS bundle (OTA or bundled asset) @@ -59,13 +59,13 @@ object ReactNativeController { LogUtils.getEnvironment(PaymentConfiguration.Companion.publishableKey()) val airborneUrl = application.getString( if (environment == SDKEnvironment.SANDBOX) - R.string.hyperOTASandBoxEndPoint + R.string.airborneSandBoxEndPoint else - R.string.hyperOTAEndPoint + R.string.airborneEndPoint ) - // Ensure OTA endpoint is valid - if (airborneUrl != "hyperOTA_END_POINT_") { + // Ensure Airborne endpoint is valid (not the default placeholder) + if (airborneUrl != "airborne_END_POINT_") { val airborneClass = Class.forName("io.hyperswitch.airborne.AirborneOTA") diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c70fd546..8e510ec9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ HyperSwitch - hyperOTA_END_POINT_ - hyperOTA_END_POINT_ + airborne_END_POINT_ + airborne_END_POINT_ \ No newline at end of file diff --git a/hyperswitch-sdk-android-airborne/build.gradle b/hyperswitch-sdk-android-airborne/build.gradle index d50a42ac..9758817f 100644 --- a/hyperswitch-sdk-android-airborne/build.gradle +++ b/hyperswitch-sdk-android-airborne/build.gradle @@ -32,6 +32,6 @@ rootProject.allprojects { } dependencies { - implementation("in.juspay:hyperotareact:0.0.1-xota.10") + implementation("in.juspay:hyperotareact:2.2.2-xota.07") implementation project(':hyperswitch-sdk-android-logger') } \ No newline at end of file diff --git a/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/HyperOtaLogger.kt b/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/AirborneLogger.kt similarity index 65% rename from hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/HyperOtaLogger.kt rename to hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/AirborneLogger.kt index a7446ec1..249bec51 100644 --- a/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/HyperOtaLogger.kt +++ b/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/AirborneLogger.kt @@ -9,14 +9,12 @@ import io.hyperswitch.logs.LogCategory import org.json.JSONException /** - * Logger implementation for tracking HyperOTA lifecycle events and errors. + * Logger implementation for tracking Airborne OTA lifecycle events and errors. * Extends TrackerCallback to capture OTA-related events and send them to the logging endpoint. * * @param sdkVersion The version of the SDK being used - * @param customLogUrl Optional custom URL for the logging endpoint - * @param publishableKey Optional merchant's publishable key for log authentication */ -class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() { +class AirborneLogger(private val sdkVersion: String) : TrackerCallback() { private fun createAndSendLog( eventName: EventName, @@ -41,55 +39,66 @@ class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() { try { val jsonData = JSONObject(eventData).toString() - val log = HSLog.LogBuilder().logType(level).category(LogCategory.OTA_LIFE_CYCLE) + val log = HSLog.LogBuilder().logType(level).category(LogCategory.AIRBORNE_LIFE_CYCLE) .eventName(eventName).value(jsonData).version(this.sdkVersion) HyperLogManager.addLog(log.build()) - } catch (e: JSONException) { + } catch (_: JSONException) { } } } /** - * Tracks OTA lifecycle events with primitive or string values. + * Resolves the appropriate EventName based on the event key. + * + * Maps lifecycle event keys from the Airborne SDK to internal event names: + * - "started" / "init" → AIRBORNE_INIT + * - "end" → AIRBORNE_FINISH + * - "boot" → AIRBORNE_BOOT + * - everything else → AIRBORNE_EVENT + */ + private fun resolveEventName(key: String): EventName { + return when (key) { + "started", "init" -> EventName.AIRBORNE_INIT + "end" -> EventName.AIRBORNE_FINISH + "boot" -> EventName.AIRBORNE_BOOT + else -> EventName.AIRBORNE_EVENT + } + } + + /** + * Tracks Airborne lifecycle events with primitive or string values. * * @param category The event category (e.g., "lifecycle") - * @param subCategory The event subcategory (e.g., "hyperota") + * @param subCategory The event subcategory (e.g., "hyperota", "hypersdk") * @param level The log level (e.g., "info", "debug", "error") * @param label A descriptive label for the event - * @param key The event key (e.g., "init", "end") + * @param key The event key (e.g., "started", "end", "boot") * @param value The event value to be logged */ override fun track( category: String, subCategory: String, level: String, label: String, key: String, value: Any ) { if (category == "lifecycle") { - if (subCategory == "hyperota") { - val eventName = when (key) { - "init" -> EventName.HYPER_OTA_INIT - "end" -> EventName.HYPER_OTA_FINISH - else -> EventName.HYPER_OTA_EVENT - } + if (subCategory == "hyperota" || subCategory == "hypersdk") { + val eventName = resolveEventName(key) createAndSendLog(eventName, level, label, category, subCategory, key, value) } else { createAndSendLog( - EventName.HYPER_OTA_EVENT, level, label, category, subCategory, key, value + EventName.AIRBORNE_EVENT, level, label, category, subCategory, key, value ) } - } -// Log.i("ota-1" , "category : " + category + "\n subCategory : " + subCategory + -// "\n level : " + level + "\n label : " + label + "\n key : " + key + "\n value : " + value.toString()) } /** - * Tracks OTA lifecycle events with JSON object values. + * Tracks Airborne lifecycle events with JSON object values. * Extracts OTA version from the value if available and updates the log manager. * * @param category The event category (e.g., "lifecycle") - * @param subCategory The event subcategory (e.g., "hyperota") + * @param subCategory The event subcategory (e.g., "hyperota", "hypersdk") * @param level The log level (e.g., "info", "debug", "error") * @param label A descriptive label for the event - * @param key The event key (e.g., "init", "end") + * @param key The event key (e.g., "started", "end", "boot") * @param value The JSON object containing event data */ override fun track( @@ -105,27 +114,20 @@ class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() { val otaVersion = value.getString("package_version") HyperLogManager.setOtaVersion(otaVersion) } catch (_: JSONException) { - } - if (subCategory == "hyperota") { - val eventName = when (key) { - "init" -> EventName.HYPER_OTA_INIT - "end" -> EventName.HYPER_OTA_FINISH - else -> EventName.HYPER_OTA_EVENT - } + if (subCategory == "hyperota" || subCategory == "hypersdk") { + val eventName = resolveEventName(key) createAndSendLog(eventName, level, label, category, subCategory, key, value) } else { createAndSendLog( - EventName.HYPER_OTA_EVENT, level, label, category, subCategory, key, value + EventName.AIRBORNE_EVENT, level, label, category, subCategory, key, value ) } } -// Log.i("ota-2" , "category : " + category + "\n subCategory : " + subCategory + -// "\n level : " + level + "\n label : " + label + "\n key : " + key + "\n value : " + value.toString()) } /** - * Tracks exceptions that occur during OTA lifecycle operations. + * Tracks exceptions that occur during Airborne lifecycle operations. * * @param category The event category (e.g., "lifecycle") * @param subCategory The event subcategory @@ -147,14 +149,12 @@ class HyperOtaLogger(private val sdkVersion: String) : TrackerCallback() { try { val jsonData = JSONObject(eventData).toString() val log = - HSLog.LogBuilder().logType("error").category(LogCategory.OTA_LIFE_CYCLE) - .eventName(EventName.HYPER_OTA_EVENT).value(jsonData) + HSLog.LogBuilder().logType("error").category(LogCategory.AIRBORNE_LIFE_CYCLE) + .eventName(EventName.AIRBORNE_EVENT).value(jsonData) HyperLogManager.addLog(log.build()) - } catch (e: JSONException) { + } catch (_: JSONException) { } } -// Log.i("ota-3" , "category : " + category + "\n subCategory : " + subCategory + -// "\n level : " + "error" + "\n label : " + label + "\n description : " + description + "\n value : " + e.toString()) } } diff --git a/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/AirborneOTA.kt b/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/AirborneOTA.kt index 6008b537..943e302a 100644 --- a/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/AirborneOTA.kt +++ b/hyperswitch-sdk-android-airborne/src/main/kotlin/io/hyperswitch/airborne/AirborneOTA.kt @@ -2,45 +2,37 @@ package io.hyperswitch.airborne import android.content.Context import androidx.annotation.Keep -import `in`.juspay.hyperota.LazyDownloadCallback -import `in`.juspay.hyperotareact.HyperOTAReact +import `in`.juspay.hyperotareact.HyperOTAServicesReact + @Keep class AirborneOTA { - private lateinit var hyperOTAReact : HyperOTAReact - private lateinit var tracker : HyperOtaLogger + private lateinit var hyperOTAServicesReact: HyperOTAServicesReact + private lateinit var tracker: AirborneLogger - fun initAirborneOTA(context: Context, - sdkVersion: String, - url : String, - appId: String, - bundleName: String){ + private fun initAirborneOTA( + context: Context, + sdkVersion: String, + url: String, + appId: String, + bundleName: String + ) { try { - this.tracker = HyperOtaLogger(sdkVersion) - HyperOTAReact( + this.tracker = AirborneLogger(sdkVersion) + hyperOTAServicesReact = HyperOTAServicesReact( context, appId, bundleName, sdkVersion, url, - mapOf( - "Content-Encoding" to "br, gzip" - ), - object : LazyDownloadCallback { - override fun fileInstalled(filePath: String, success: Boolean) { - } - - override fun lazySplitsInstalled(success: Boolean) { - } - }, - tracker, + tracker ) - }catch (e: Exception){ + } catch (e: Exception) { e.printStackTrace() } } /** - * constructor for HyperOTA with full configuration. + * Constructor for AirborneOTA with full configuration. * * @param context The Android application context * @param sdkVersion The version of the SDK being used @@ -48,27 +40,30 @@ class AirborneOTA { * @param appId The application identifier for OTA updates * @param bundleName The name of the bundle to be downloaded and installed */ - constructor(context: Context, - sdkVersion: String, - configUrl : String, - appId: String, - bundleName: String - ){ - this.initAirborneOTA(context,sdkVersion,configUrl,appId,bundleName) + constructor( + context: Context, + sdkVersion: String, + configUrl: String, + appId: String, + bundleName: String + ) { + this.initAirborneOTA(context, sdkVersion, configUrl, appId, bundleName) } /** - * constructor for HyperOTA with default Hyperswitch configuration. + * Constructor for AirborneOTA with default Hyperswitch configuration. * Automatically constructs the config URL, app ID, and bundle name. * * @param context The Android application context * @param sdkVersion The version of the SDK being used * @param baseUrl The base URL for the OTA service (config URL will be constructed as: baseUrl/mobile-ota/android/sdkVersion/config.json) */ - constructor(context: Context, - sdkVersion: String, - baseUrl : String){ - if(baseUrl == "") { + constructor( + context: Context, + sdkVersion: String, + baseUrl: String + ) { + if (baseUrl == "") { throw Exception("BaseURL shouldn't be empty") } this.initAirborneOTA( @@ -82,11 +77,10 @@ class AirborneOTA { fun getBundlePath(): String { return try { - hyperOTAReact.getBundlePath().takeUnless { it.contains("ios") } + hyperOTAServicesReact.getBundlePath().takeUnless { it.contains("ios") } ?: "assets://hyperswitch.bundle" } catch (_: Exception) { "assets://hyperswitch.bundle" } - } -} \ No newline at end of file +} diff --git a/hyperswitch-sdk-android-logger/src/main/kotlin/io/hyperswitch/logs/LogData.kt b/hyperswitch-sdk-android-logger/src/main/kotlin/io/hyperswitch/logs/LogData.kt index 119db0d1..fcbe58b2 100644 --- a/hyperswitch-sdk-android-logger/src/main/kotlin/io/hyperswitch/logs/LogData.kt +++ b/hyperswitch-sdk-android-logger/src/main/kotlin/io/hyperswitch/logs/LogData.kt @@ -8,11 +8,11 @@ enum class LogType { } enum class LogCategory { - API, USER_ERROR, USER_EVENT, MERCHANT_EVENT, OTA_LIFE_CYCLE + API, USER_ERROR, USER_EVENT, MERCHANT_EVENT, OTA_LIFE_CYCLE, AIRBORNE_LIFE_CYCLE } enum class EventName { - HYPER_OTA_INIT, HYPER_OTA_FINISH, HYPER_OTA_EVENT, CRASH_EVENT, + AIRBORNE_INIT, AIRBORNE_FINISH, AIRBORNE_BOOT, AIRBORNE_EVENT, CRASH_EVENT, AUTHENTICATION_SESSION, AUTHENTICATION_SESSION_INIT, AUTHENTICATION_SESSION_RETURNED,