diff --git a/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/SparklingContext.kt b/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/SparklingContext.kt index 5de35eab..2509701a 100644 --- a/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/SparklingContext.kt +++ b/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/SparklingContext.kt @@ -7,11 +7,19 @@ import android.content.Context import android.util.Size import android.view.View import androidx.appcompat.widget.Toolbar +import com.lynx.tasm.LynxView import com.tiktok.sparkling.hybridkit.HybridContext import com.tiktok.sparkling.hybridkit.base.HybridKitError import com.tiktok.sparkling.hybridkit.base.HybridKitType import com.tiktok.sparkling.hybridkit.base.IKitView +/** + * Observes a created Lynx view after Sparkling initializes its bridge and before template loading. + */ +fun interface SparklingLynxViewCreatedListener { + fun onCreated(lynxView: LynxView) +} + interface SparklingUIProvider { fun getLoadingView(context: Context): View @@ -94,4 +102,5 @@ interface SparklingLifecycleDelegate { class SparklingContext : HybridContext() { var sparklingUIProvider: SparklingUIProvider? = null var lifecycleDelegate: SparklingLifecycleDelegate? = null + var lynxViewCreatedListener: SparklingLynxViewCreatedListener? = null } diff --git a/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/hybridkit/lynx/HybridLynxKit.kt b/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/hybridkit/lynx/HybridLynxKit.kt index 82639c33..96a559fe 100644 --- a/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/hybridkit/lynx/HybridLynxKit.kt +++ b/packages/sparkling-sdk/android/sparkling/src/main/java/com/tiktok/sparkling/hybridkit/lynx/HybridLynxKit.kt @@ -14,6 +14,7 @@ import com.lynx.tasm.LynxViewBuilder import com.lynx.tasm.behavior.Behavior import com.lynx.tasm.behavior.BehaviorBundle import com.lynx.tasm.service.LynxServiceCenter +import com.tiktok.sparkling.SparklingContext import com.tiktok.sparkling.hybridkit.HybridCommon import com.tiktok.sparkling.hybridkit.HybridContext import com.tiktok.sparkling.hybridkit.base.IHybridKitLifeCycle @@ -106,6 +107,7 @@ object HybridLynxKit { } val bridge = SparklingBridge() bridge.registerLynxModule(viewBuilder, hybridContext.containerId) + val sparklingContext = hybridContext as? SparklingContext val lynxView = SimpleLynxKitView(context, hybridContext, viewBuilder, kitInitParams, lifeCycle) lynxViewRef = lynxView @@ -115,6 +117,7 @@ object HybridLynxKit { BridgeProtocolConstants.BRIDGE_LYNX_PROTOCOL, ) hybridContext.bridge = bridge + sparklingContext?.lynxViewCreatedListener?.onCreated(lynxView) lifeCycle?.onPostKitCreated(lynxView) return lynxView diff --git a/packages/sparkling-sdk/android/sparkling/src/test/java/com/tiktok/sparkling/hybridkit/HybridKitTest.kt b/packages/sparkling-sdk/android/sparkling/src/test/java/com/tiktok/sparkling/hybridkit/HybridKitTest.kt index 3f23ae1d..d087a1a0 100644 --- a/packages/sparkling-sdk/android/sparkling/src/test/java/com/tiktok/sparkling/hybridkit/HybridKitTest.kt +++ b/packages/sparkling-sdk/android/sparkling/src/test/java/com/tiktok/sparkling/hybridkit/HybridKitTest.kt @@ -10,11 +10,15 @@ import android.content.res.Resources import android.view.WindowManager import android.view.Display import android.view.accessibility.AccessibilityManager +import com.lynx.tasm.LynxView +import com.tiktok.sparkling.SparklingContext +import com.tiktok.sparkling.SparklingLynxViewCreatedListener import com.tiktok.sparkling.hybridkit.HybridContext import com.tiktok.sparkling.hybridkit.HybridKit import com.tiktok.sparkling.hybridkit.HybridCommon import com.tiktok.sparkling.hybridkit.HybridEnvironment import com.tiktok.sparkling.hybridkit.KitViewManager +import com.tiktok.sparkling.hybridkit.base.IHybridKitLifeCycle import com.tiktok.sparkling.hybridkit.base.HybridKitType import com.tiktok.sparkling.hybridkit.base.HybridLoadSession import com.tiktok.sparkling.hybridkit.base.Theme @@ -118,6 +122,62 @@ class HybridKitTest { assertNull(result) } + + @Test + fun testLynxViewCreatedListenerRunsAfterBridgeBeforePostCreate() { + val events = mutableListOf() + var bridgeAvailable = false + var createdView: LynxView? = null + val scheme = + HybridSchemeParam( + engineType = HybridKitType.LYNX, + bundle = "https://example.com/main.lynx.bundle", + ) + val sparklingContext = + SparklingContext().apply { + hybridSchemeParam = scheme + lynxViewCreatedListener = + SparklingLynxViewCreatedListener { lynxView -> + events += "VIEW_CREATED" + createdView = lynxView + bridgeAvailable = bridge != null + } + } + val lifeCycle = + object : IHybridKitLifeCycle() { + override fun onPostKitCreated(view: com.tiktok.sparkling.hybridkit.base.IKitView) { + events += "POST_KIT_CREATED" + } + } + + val result = HybridKit.createKitView(scheme, sparklingContext, mockContext, lifeCycle) + + assertEquals( + listOf( + "VIEW_CREATED", + "POST_KIT_CREATED", + ), + events, + ) + assertTrue(bridgeAvailable) + assertSame(result, createdView) + } + + @Test + fun testLynxViewCreatedListenerIsDisabledByDefault() { + val scheme = + HybridSchemeParam( + engineType = HybridKitType.LYNX, + bundle = "https://example.com/main.lynx.bundle", + ) + val sparklingContext = + SparklingContext().apply { + hybridSchemeParam = scheme + } + + assertNull(sparklingContext.lynxViewCreatedListener) + assertNotNull(HybridKit.createKitView(scheme, sparklingContext, mockContext)) + } } @RunWith(RobolectricTestRunner::class)