@shortkitsdk/react-native 0.2.37 → 0.2.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle.kts +4 -4
- package/android/libs/shortkit-release.aar +0 -0
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +8 -6
- package/android/src/main/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHost.kt +24 -10
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +2 -1
- package/android/src/test/java/com/shortkit/reactnative/ReactCarouselOverlayHostLifecycleTest.kt +136 -0
- package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostLifecycleTest.kt +178 -0
- package/ios/ShortKitSDK.xcframework/Info.plist +5 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +1 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +3 -3
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist +2 -2
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +1 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json +1 -1
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +5 -5
- package/package.json +1 -1
package/android/build.gradle.kts
CHANGED
|
@@ -35,10 +35,10 @@ android {
|
|
|
35
35
|
|
|
36
36
|
dependencies {
|
|
37
37
|
implementation("com.facebook.react:react-android")
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
implementation(files("libs/shortkit-release.aar"))
|
|
38
|
+
// Composite build in sample/android/settings.gradle substitutes this with
|
|
39
|
+
// live android_sdk source automatically (dev.shortkit:shortkit → :shortkit project).
|
|
40
|
+
implementation("dev.shortkit:shortkit:0.2.37")
|
|
41
|
+
// implementation(files("libs/shortkit-release.aar"))
|
|
42
42
|
// Transitive deps needed when using local AAR (Maven artifact bundles these automatically)
|
|
43
43
|
implementation("androidx.media3:media3-exoplayer:1.5.1")
|
|
44
44
|
implementation("androidx.media3:media3-exoplayer-hls:1.5.1")
|
|
Binary file
|
|
@@ -343,8 +343,9 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
override fun resetState() {
|
|
346
|
-
|
|
347
|
-
|
|
346
|
+
// Non-destructive: configure() will push new props on next bind.
|
|
347
|
+
// iOS parity: ReactCarouselOverlayHost.swift resetState() is a no-op.
|
|
348
|
+
// Mirrors ReactVideoCarouselOverlayHost.resetState() which is also a no-op.
|
|
348
349
|
}
|
|
349
350
|
|
|
350
351
|
override fun fadeOutForTransition() {
|
|
@@ -464,12 +465,13 @@ class ReactCarouselOverlayHost(context: Context) : FrameLayout(context), Carouse
|
|
|
464
465
|
// Cleanup
|
|
465
466
|
// ------------------------------------------------------------------
|
|
466
467
|
|
|
467
|
-
override fun onDetachedFromWindow() {
|
|
468
|
+
public override fun onDetachedFromWindow() {
|
|
468
469
|
super.onDetachedFromWindow()
|
|
469
470
|
pendingWriteJob?.cancel()
|
|
470
471
|
pendingWriteJob = null
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
472
|
+
// Non-destructive: surface survives cell recycle and view transitions.
|
|
473
|
+
// iOS parity: ReactCarouselOverlayHost.swift has no willMove/didMoveToWindow
|
|
474
|
+
// override — surface stops only in deinit. Mirrors the non-destructive
|
|
475
|
+
// contract enforced on ReactVideoCarouselOverlayHost.
|
|
474
476
|
}
|
|
475
477
|
}
|
|
@@ -133,7 +133,8 @@ class ReactVideoCarouselOverlayHost(context: Context) : FrameLayout(context), Vi
|
|
|
133
133
|
|
|
134
134
|
// Player state
|
|
135
135
|
private var player: ShortKitPlayer? = null
|
|
136
|
-
|
|
136
|
+
@androidx.annotation.VisibleForTesting
|
|
137
|
+
internal var isActive: Boolean = false
|
|
137
138
|
private var cachedPlayerState: String = "idle"
|
|
138
139
|
private var cachedIsMuted: Boolean = true
|
|
139
140
|
private var cachedCurrentTime: Double = 0.0
|
|
@@ -630,15 +631,28 @@ class ReactVideoCarouselOverlayHost(context: Context) : FrameLayout(context), Vi
|
|
|
630
631
|
// Cleanup
|
|
631
632
|
// ------------------------------------------------------------------
|
|
632
633
|
|
|
633
|
-
override fun onDetachedFromWindow() {
|
|
634
|
+
public override fun onDetachedFromWindow() {
|
|
634
635
|
super.onDetachedFromWindow()
|
|
635
|
-
|
|
636
|
-
flowScope
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
636
|
+
// iOS-parity: detach is fully non-destructive. The React surface,
|
|
637
|
+
// currentCarouselItem, flowScope, isActive, AND the time-coalescing
|
|
638
|
+
// runnable all survive visibility transitions. The cell's recycle
|
|
639
|
+
// path keeps the overlay attached (mirror of iOS
|
|
640
|
+
// VideoCarouselCell.prepareForReuse).
|
|
641
|
+
//
|
|
642
|
+
// isActive is owned exclusively by the feed's active-cell
|
|
643
|
+
// propagation (activatePlayback / deactivatePlayback). Touching it
|
|
644
|
+
// here would create a second writer and break the suspend/resume
|
|
645
|
+
// case where the active cell never changes — activatePlayback
|
|
646
|
+
// wouldn't re-fire on resume, leaving isActive stuck false and
|
|
647
|
+
// gating out every updateActiveVideo emit.
|
|
648
|
+
//
|
|
649
|
+
// Player flow subscriptions are idempotently re-established by
|
|
650
|
+
// attach() -> resubscribeToPlayer (which cancels the prior scope
|
|
651
|
+
// on entry) when the cell rebinds its player on next visit.
|
|
652
|
+
//
|
|
653
|
+
// True teardown happens via release() (called by the cell on
|
|
654
|
+
// permanent disposal) or implicitly when the host is GC'd along
|
|
655
|
+
// with the cell.
|
|
643
656
|
}
|
|
657
|
+
|
|
644
658
|
}
|
|
@@ -584,7 +584,8 @@ class ShortKitBridge(
|
|
|
584
584
|
customDimensions = dims,
|
|
585
585
|
debugPanelEnabled = debugPanel,
|
|
586
586
|
serverTracingEnabled = serverTracingEnabled,
|
|
587
|
-
consoleTracingEnabled = consoleTracingEnabled
|
|
587
|
+
consoleTracingEnabled = consoleTracingEnabled,
|
|
588
|
+
poolDebugEnabled = consoleTracingEnabled,
|
|
588
589
|
)
|
|
589
590
|
this.shortKit = sdk
|
|
590
591
|
shared = this
|
package/android/src/test/java/com/shortkit/reactnative/ReactCarouselOverlayHostLifecycleTest.kt
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
package com.shortkit.reactnative
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.ViewGroup
|
|
5
|
+
import androidx.test.core.app.ApplicationProvider
|
|
6
|
+
import com.facebook.react.bridge.JavaOnlyMap
|
|
7
|
+
import com.facebook.react.interfaces.TaskInterface
|
|
8
|
+
import com.facebook.react.interfaces.fabric.ReactSurface
|
|
9
|
+
import com.shortkit.sdk.model.CarouselImage
|
|
10
|
+
import com.shortkit.sdk.model.ImageCarouselItem
|
|
11
|
+
import org.junit.Assert.assertEquals
|
|
12
|
+
import org.junit.Assert.assertNotNull
|
|
13
|
+
import org.junit.Assert.assertTrue
|
|
14
|
+
import org.junit.Test
|
|
15
|
+
import org.junit.runner.RunWith
|
|
16
|
+
import org.robolectric.RobolectricTestRunner
|
|
17
|
+
import java.util.concurrent.TimeUnit
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Lifecycle contract for ReactCarouselOverlayHost: surface must survive
|
|
21
|
+
* onDetachedFromWindow / onAttachedToWindow cycles (iOS-parity), and
|
|
22
|
+
* resetState() must be a no-op.
|
|
23
|
+
*
|
|
24
|
+
* iOS reference: ReactCarouselOverlayHost.swift has no willMove/didMoveToWindow
|
|
25
|
+
* override — surface only stops in deinit. resetState() is also a no-op on iOS:
|
|
26
|
+
* "Don't clear surface props — configure() will overwrite."
|
|
27
|
+
*
|
|
28
|
+
* Mirrors the contract enforced by ReactVideoCarouselOverlayHostLifecycleTest
|
|
29
|
+
* for the video carousel host.
|
|
30
|
+
*/
|
|
31
|
+
@RunWith(RobolectricTestRunner::class)
|
|
32
|
+
class ReactCarouselOverlayHostLifecycleTest {
|
|
33
|
+
|
|
34
|
+
private object NoOpTask : TaskInterface<Void> {
|
|
35
|
+
override fun waitForCompletion() = Unit
|
|
36
|
+
override fun waitForCompletion(duration: Long, timeUnit: TimeUnit): Boolean = true
|
|
37
|
+
override fun getResult(): Void? = null
|
|
38
|
+
override fun getError(): Exception? = null
|
|
39
|
+
override fun isCompleted(): Boolean = true
|
|
40
|
+
override fun isCancelled(): Boolean = false
|
|
41
|
+
override fun isFaulted(): Boolean = false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Mutable-state ReactSurface stub tracking isRunning and stop call count.
|
|
46
|
+
* Mirrors TrackingSurface in ReactVideoCarouselOverlayHostLifecycleTest.
|
|
47
|
+
*/
|
|
48
|
+
private class TrackingSurface : ReactSurface {
|
|
49
|
+
private val ctx: Context = ApplicationProvider.getApplicationContext()
|
|
50
|
+
private var _running: Boolean = false
|
|
51
|
+
var stopCount: Int = 0
|
|
52
|
+
|
|
53
|
+
override val surfaceID: Int = 0
|
|
54
|
+
override val moduleName: String = "TrackingSurface"
|
|
55
|
+
override val isRunning: Boolean get() = _running
|
|
56
|
+
override val view: ViewGroup? = null
|
|
57
|
+
override val context: Context get() = ctx
|
|
58
|
+
override fun prerender(): TaskInterface<Void> = NoOpTask
|
|
59
|
+
override fun start(): TaskInterface<Void> { _running = true; return NoOpTask }
|
|
60
|
+
override fun stop(): TaskInterface<Void> { _running = false; stopCount++; return NoOpTask }
|
|
61
|
+
override fun clear() = Unit
|
|
62
|
+
override fun detach() = Unit
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private fun makeHost(surface: TrackingSurface): ReactCarouselOverlayHost {
|
|
66
|
+
val ctx = ApplicationProvider.getApplicationContext<Context>()
|
|
67
|
+
return ReactCarouselOverlayHost(ctx).also {
|
|
68
|
+
it.createMap = { JavaOnlyMap() }
|
|
69
|
+
it.createSurface = { _, _ -> surface }
|
|
70
|
+
it.emitEvent = { _, _ -> }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private fun item(id: String) = ImageCarouselItem(
|
|
75
|
+
id = id,
|
|
76
|
+
images = listOf(CarouselImage(url = "https://example.com/$id.jpg", alt = null)),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@Test
|
|
80
|
+
fun `onDetachedFromWindow does not stop surface`() {
|
|
81
|
+
val surface = TrackingSurface()
|
|
82
|
+
val host = makeHost(surface)
|
|
83
|
+
host.configure(item("A"))
|
|
84
|
+
assertTrue("surface must be running after configure", surface.isRunning)
|
|
85
|
+
val stopsBefore = surface.stopCount
|
|
86
|
+
|
|
87
|
+
host.onDetachedFromWindow()
|
|
88
|
+
|
|
89
|
+
assertTrue(
|
|
90
|
+
"surface must remain running after onDetachedFromWindow (iOS-parity)",
|
|
91
|
+
surface.isRunning,
|
|
92
|
+
)
|
|
93
|
+
assertEquals(
|
|
94
|
+
"onDetachedFromWindow must not call surface.stop()",
|
|
95
|
+
stopsBefore,
|
|
96
|
+
surface.stopCount,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@Test
|
|
101
|
+
fun `onDetachedFromWindow preserves currentItemId`() {
|
|
102
|
+
val surface = TrackingSurface()
|
|
103
|
+
val host = makeHost(surface)
|
|
104
|
+
host.configure(item("A"))
|
|
105
|
+
assertEquals("A", host.currentItemId)
|
|
106
|
+
|
|
107
|
+
host.onDetachedFromWindow()
|
|
108
|
+
|
|
109
|
+
assertEquals(
|
|
110
|
+
"currentItemId must survive onDetachedFromWindow so configure() can detect same vs different item",
|
|
111
|
+
"A",
|
|
112
|
+
host.currentItemId,
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@Test
|
|
117
|
+
fun `resetState does not stop surface`() {
|
|
118
|
+
// resetState() must be a no-op: iOS ReactCarouselOverlayHost.swift
|
|
119
|
+
// resetState() body is "// Don't clear surface props — configure() will overwrite."
|
|
120
|
+
// Mirrors ReactVideoCarouselOverlayHost.resetState() which is also a no-op.
|
|
121
|
+
// Note: the current production cast (surface as? ReactSurfaceImpl) silently
|
|
122
|
+
// no-ops in Robolectric (stub isn't ReactSurfaceImpl), so this test
|
|
123
|
+
// documents intent and guards against future regressions that add
|
|
124
|
+
// a surface?.stop() call.
|
|
125
|
+
val surface = TrackingSurface()
|
|
126
|
+
val host = makeHost(surface)
|
|
127
|
+
host.configure(item("A"))
|
|
128
|
+
assertTrue("surface must be running after configure", surface.isRunning)
|
|
129
|
+
val stopsBefore = surface.stopCount
|
|
130
|
+
|
|
131
|
+
host.resetState()
|
|
132
|
+
|
|
133
|
+
assertTrue("surface must remain running after resetState", surface.isRunning)
|
|
134
|
+
assertEquals("resetState must not call surface.stop()", stopsBefore, surface.stopCount)
|
|
135
|
+
}
|
|
136
|
+
}
|
package/android/src/test/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHostLifecycleTest.kt
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
package com.shortkit.reactnative
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.ViewGroup
|
|
5
|
+
import androidx.test.core.app.ApplicationProvider
|
|
6
|
+
import com.facebook.react.bridge.JavaOnlyMap
|
|
7
|
+
import com.facebook.react.interfaces.TaskInterface
|
|
8
|
+
import com.facebook.react.interfaces.fabric.ReactSurface
|
|
9
|
+
import com.shortkit.sdk.model.ContentItem
|
|
10
|
+
import com.shortkit.sdk.model.VideoCarouselItem
|
|
11
|
+
import org.junit.Assert.assertEquals
|
|
12
|
+
import org.junit.Assert.assertFalse
|
|
13
|
+
import org.junit.Assert.assertNotNull
|
|
14
|
+
import org.junit.Assert.assertTrue
|
|
15
|
+
import org.junit.Test
|
|
16
|
+
import org.junit.runner.RunWith
|
|
17
|
+
import org.robolectric.RobolectricTestRunner
|
|
18
|
+
import java.util.concurrent.TimeUnit
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Lifecycle contract for ReactVideoCarouselOverlayHost: surface and state
|
|
22
|
+
* must survive onDetachedFromWindow / onAttachedToWindow cycles
|
|
23
|
+
* (iOS-parity), and only release() should perform true teardown.
|
|
24
|
+
*
|
|
25
|
+
* iOS reference: swift_sdk/.../VideoCarouselCell.swift:472-476 keeps the
|
|
26
|
+
* overlay alive across prepareForReuse; the host's deinit performs final
|
|
27
|
+
* cleanup. Android equivalent: this lifecycle test enforces the same
|
|
28
|
+
* contract on the RN host.
|
|
29
|
+
*/
|
|
30
|
+
@RunWith(RobolectricTestRunner::class)
|
|
31
|
+
class ReactVideoCarouselOverlayHostLifecycleTest {
|
|
32
|
+
|
|
33
|
+
private object NoOpTask : TaskInterface<Void> {
|
|
34
|
+
override fun waitForCompletion() = Unit
|
|
35
|
+
override fun waitForCompletion(duration: Long, timeUnit: TimeUnit): Boolean = true
|
|
36
|
+
override fun getResult(): Void? = null
|
|
37
|
+
override fun getError(): Exception? = null
|
|
38
|
+
override fun isCompleted(): Boolean = true
|
|
39
|
+
override fun isCancelled(): Boolean = false
|
|
40
|
+
override fun isFaulted(): Boolean = false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Mutable-state ReactSurface stub that tracks isRunning + start/stop
|
|
45
|
+
* calls. Mirrors the StubReactSurface shape used by
|
|
46
|
+
* ReactVideoCarouselOverlayHostEmitTest, but with mutable state via
|
|
47
|
+
* a backing field + custom getter (the interface declares isRunning
|
|
48
|
+
* as `val`, so we can't override with `var`).
|
|
49
|
+
*/
|
|
50
|
+
private class TrackingSurface : ReactSurface {
|
|
51
|
+
private val ctx: Context = ApplicationProvider.getApplicationContext()
|
|
52
|
+
private var _running: Boolean = false
|
|
53
|
+
var stopCount: Int = 0
|
|
54
|
+
|
|
55
|
+
override val surfaceID: Int = 0
|
|
56
|
+
override val moduleName: String = "TrackingSurface"
|
|
57
|
+
override val isRunning: Boolean get() = _running
|
|
58
|
+
override val view: ViewGroup? = null
|
|
59
|
+
override val context: Context get() = ctx
|
|
60
|
+
override fun prerender(): TaskInterface<Void> = NoOpTask
|
|
61
|
+
override fun start(): TaskInterface<Void> { _running = true; return NoOpTask }
|
|
62
|
+
override fun stop(): TaskInterface<Void> { _running = false; stopCount++; return NoOpTask }
|
|
63
|
+
override fun clear() = Unit
|
|
64
|
+
override fun detach() = Unit
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private fun newHost(surface: TrackingSurface): ReactVideoCarouselOverlayHost {
|
|
68
|
+
val host = ReactVideoCarouselOverlayHost(
|
|
69
|
+
ApplicationProvider.getApplicationContext()
|
|
70
|
+
)
|
|
71
|
+
host.videoCarouselOverlayName = "test"
|
|
72
|
+
host.createSurface = { _, _ -> surface }
|
|
73
|
+
host.createMap = { JavaOnlyMap() }
|
|
74
|
+
host.emitEvent = { _, _ -> /* no-op for lifecycle tests */ }
|
|
75
|
+
return host
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private fun video(id: String) = ContentItem(
|
|
79
|
+
id = id,
|
|
80
|
+
title = "t",
|
|
81
|
+
duration = 5.0,
|
|
82
|
+
streamingUrl = "https://example.com/$id.m3u8",
|
|
83
|
+
thumbnailUrl = "https://example.com/$id.jpg",
|
|
84
|
+
captionTracks = emptyList(),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
private fun item(id: String, videoCount: Int = 1) = VideoCarouselItem(
|
|
88
|
+
id = id,
|
|
89
|
+
videos = (0 until videoCount).map { video("$id-$it") },
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@Test
|
|
93
|
+
fun `onDetachedFromWindow does not stop surface`() {
|
|
94
|
+
val surface = TrackingSurface()
|
|
95
|
+
val host = newHost(surface)
|
|
96
|
+
host.configure(item("c1")) // first mount: applies props, starts surface
|
|
97
|
+
assertTrue("surface must be running after configure", surface.isRunning)
|
|
98
|
+
val stopsBefore = surface.stopCount
|
|
99
|
+
|
|
100
|
+
host.onDetachedFromWindow()
|
|
101
|
+
|
|
102
|
+
assertTrue(
|
|
103
|
+
"surface must remain running after onDetachedFromWindow (iOS-parity)",
|
|
104
|
+
surface.isRunning,
|
|
105
|
+
)
|
|
106
|
+
assertEquals(
|
|
107
|
+
"onDetachedFromWindow must not call surface.stop()",
|
|
108
|
+
stopsBefore,
|
|
109
|
+
surface.stopCount,
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@Test
|
|
114
|
+
fun `onDetachedFromWindow preserves currentCarouselItem`() {
|
|
115
|
+
val surface = TrackingSurface()
|
|
116
|
+
val host = newHost(surface)
|
|
117
|
+
host.configure(item("c1"))
|
|
118
|
+
assertNotNull(host.currentCarouselItem)
|
|
119
|
+
|
|
120
|
+
host.onDetachedFromWindow()
|
|
121
|
+
|
|
122
|
+
assertNotNull(
|
|
123
|
+
"currentCarouselItem must survive onDetachedFromWindow so re-attach can detect same vs different item",
|
|
124
|
+
host.currentCarouselItem,
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@Test
|
|
129
|
+
fun `onDetachedFromWindow preserves isActive`() {
|
|
130
|
+
// Suspend/resume scenario: active cell never changes, so the feed's
|
|
131
|
+
// active-cell propagation does not re-fire activatePlayback on
|
|
132
|
+
// resume. If detach flipped isActive false, every subsequent
|
|
133
|
+
// updateActiveVideo emit would be gated out and per-page swipes
|
|
134
|
+
// wouldn't update the overlay. iOS-parity: isActive is owned
|
|
135
|
+
// exclusively by activatePlayback / deactivatePlayback; lifecycle
|
|
136
|
+
// hooks must not touch it.
|
|
137
|
+
val surface = TrackingSurface()
|
|
138
|
+
val host = newHost(surface)
|
|
139
|
+
host.configure(item("c1"))
|
|
140
|
+
host.activatePlayback()
|
|
141
|
+
assertTrue(host.isActive)
|
|
142
|
+
|
|
143
|
+
host.onDetachedFromWindow()
|
|
144
|
+
|
|
145
|
+
assertTrue(
|
|
146
|
+
"isActive must survive onDetachedFromWindow — only activatePlayback/deactivatePlayback own it",
|
|
147
|
+
host.isActive,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@Test
|
|
152
|
+
fun `onDetachedFromWindow does not stop time coalescer`() {
|
|
153
|
+
// If detach stopped the coalescer, on resume nothing restarts it
|
|
154
|
+
// (activatePlayback is the only caller and doesn't re-fire when
|
|
155
|
+
// the active cell never changed). Time emits would silently stop.
|
|
156
|
+
// Mirrors the isActive ownership rule: detach is a no-op for
|
|
157
|
+
// playback-driven state.
|
|
158
|
+
val surface = TrackingSurface()
|
|
159
|
+
val host = newHost(surface)
|
|
160
|
+
host.configure(item("c1"))
|
|
161
|
+
host.activatePlayback()
|
|
162
|
+
// activatePlayback() is what kicks off time coalescing — once
|
|
163
|
+
// running, it should survive detach.
|
|
164
|
+
|
|
165
|
+
// No assertion on internals; the contract is observable via the
|
|
166
|
+
// updateActiveVideo regression test above. This test exists to
|
|
167
|
+
// document the non-destructive-detach intent: onDetachedFromWindow
|
|
168
|
+
// must not call stopTimeCoalescing().
|
|
169
|
+
host.onDetachedFromWindow()
|
|
170
|
+
|
|
171
|
+
// host.isActive still true => the coalesce runnable would still
|
|
172
|
+
// tick if it weren't cancelled. We cannot easily inspect the
|
|
173
|
+
// handler queue from here, but we can assert the public state
|
|
174
|
+
// hasn't been disturbed:
|
|
175
|
+
assertTrue("isActive untouched", host.isActive)
|
|
176
|
+
assertNotNull("currentCarouselItem untouched", host.currentCarouselItem)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>ShortKitSDK.framework/ShortKitSDK</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>ShortKitSDK.framework</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
+
<string>x86_64</string>
|
|
17
18
|
</array>
|
|
18
19
|
<key>SupportedPlatform</key>
|
|
19
20
|
<string>ios</string>
|
|
21
|
+
<key>SupportedPlatformVariant</key>
|
|
22
|
+
<string>simulator</string>
|
|
20
23
|
</dict>
|
|
21
24
|
<dict>
|
|
22
25
|
<key>BinaryPath</key>
|
|
23
26
|
<string>ShortKitSDK.framework/ShortKitSDK</string>
|
|
24
27
|
<key>LibraryIdentifier</key>
|
|
25
|
-
<string>ios-
|
|
28
|
+
<string>ios-arm64</string>
|
|
26
29
|
<key>LibraryPath</key>
|
|
27
30
|
<string>ShortKitSDK.framework</string>
|
|
28
31
|
<key>SupportedArchitectures</key>
|
|
29
32
|
<array>
|
|
30
33
|
<string>arm64</string>
|
|
31
|
-
<string>x86_64</string>
|
|
32
34
|
</array>
|
|
33
35
|
<key>SupportedPlatform</key>
|
|
34
36
|
<string>ios</string>
|
|
35
|
-
<key>SupportedPlatformVariant</key>
|
|
36
|
-
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
<key>CFBundlePackageType</key>
|
|
12
12
|
<string>FMWK</string>
|
|
13
13
|
<key>CFBundleVersion</key>
|
|
14
|
-
<string>0.2.
|
|
14
|
+
<string>0.2.38</string>
|
|
15
15
|
<key>CFBundleShortVersionString</key>
|
|
16
|
-
<string>0.2.
|
|
16
|
+
<string>0.2.38</string>
|
|
17
17
|
<key>MinimumOSVersion</key>
|
|
18
18
|
<string>16.0</string>
|
|
19
19
|
</dict>
|
|
@@ -53517,7 +53517,7 @@
|
|
|
53517
53517
|
"kind": "StringLiteral",
|
|
53518
53518
|
"offset": 154,
|
|
53519
53519
|
"length": 8,
|
|
53520
|
-
"value": "\"0.2.
|
|
53520
|
+
"value": "\"0.2.38\""
|
|
53521
53521
|
},
|
|
53522
53522
|
{
|
|
53523
53523
|
"filePath": "\/Users\/michaelseleman\/shortkit\/swift_sdk\/Sources\/ShortKit\/ShortKit.swift",
|
|
Binary file
|
package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
</data>
|
|
11
11
|
<key>Info.plist</key>
|
|
12
12
|
<data>
|
|
13
|
-
|
|
13
|
+
OPbiGQgVvhc9i76qqjEwO15NZ9U=
|
|
14
14
|
</data>
|
|
15
15
|
<key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json</key>
|
|
16
16
|
<data>
|
|
17
|
-
|
|
17
|
+
bmR7HZYLcS/c/oJglUFjXRw3kcs=
|
|
18
18
|
</data>
|
|
19
19
|
<key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface</key>
|
|
20
20
|
<data>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
<dict>
|
|
51
51
|
<key>hash2</key>
|
|
52
52
|
<data>
|
|
53
|
-
|
|
53
|
+
k2NIGYptDbpRKu5vmsmuboSr7G2j4qb9eftlewub0LA=
|
|
54
54
|
</data>
|
|
55
55
|
</dict>
|
|
56
56
|
<key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface</key>
|
package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/Info.plist
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
<key>CFBundlePackageType</key>
|
|
12
12
|
<string>FMWK</string>
|
|
13
13
|
<key>CFBundleVersion</key>
|
|
14
|
-
<string>0.2.
|
|
14
|
+
<string>0.2.38</string>
|
|
15
15
|
<key>CFBundleShortVersionString</key>
|
|
16
|
-
<string>0.2.
|
|
16
|
+
<string>0.2.38</string>
|
|
17
17
|
<key>MinimumOSVersion</key>
|
|
18
18
|
<string>16.0</string>
|
|
19
19
|
</dict>
|
|
@@ -53517,7 +53517,7 @@
|
|
|
53517
53517
|
"kind": "StringLiteral",
|
|
53518
53518
|
"offset": 154,
|
|
53519
53519
|
"length": 8,
|
|
53520
|
-
"value": "\"0.2.
|
|
53520
|
+
"value": "\"0.2.38\""
|
|
53521
53521
|
},
|
|
53522
53522
|
{
|
|
53523
53523
|
"filePath": "\/Users\/michaelseleman\/shortkit\/swift_sdk\/Sources\/ShortKit\/ShortKit.swift",
|
|
@@ -53517,7 +53517,7 @@
|
|
|
53517
53517
|
"kind": "StringLiteral",
|
|
53518
53518
|
"offset": 154,
|
|
53519
53519
|
"length": 8,
|
|
53520
|
-
"value": "\"0.2.
|
|
53520
|
+
"value": "\"0.2.38\""
|
|
53521
53521
|
},
|
|
53522
53522
|
{
|
|
53523
53523
|
"filePath": "\/Users\/michaelseleman\/shortkit\/swift_sdk\/Sources\/ShortKit\/ShortKit.swift",
|
package/ios/ShortKitSDK.xcframework/ios-arm64_x86_64-simulator/ShortKitSDK.framework/ShortKitSDK
CHANGED
|
Binary file
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
</data>
|
|
11
11
|
<key>Info.plist</key>
|
|
12
12
|
<data>
|
|
13
|
-
|
|
13
|
+
OPbiGQgVvhc9i76qqjEwO15NZ9U=
|
|
14
14
|
</data>
|
|
15
15
|
<key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json</key>
|
|
16
16
|
<data>
|
|
17
|
-
|
|
17
|
+
bmR7HZYLcS/c/oJglUFjXRw3kcs=
|
|
18
18
|
</data>
|
|
19
19
|
<key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
|
|
20
20
|
<data>
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
</data>
|
|
31
31
|
<key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.abi.json</key>
|
|
32
32
|
<data>
|
|
33
|
-
|
|
33
|
+
bmR7HZYLcS/c/oJglUFjXRw3kcs=
|
|
34
34
|
</data>
|
|
35
35
|
<key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
|
|
36
36
|
<data>
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<dict>
|
|
67
67
|
<key>hash2</key>
|
|
68
68
|
<data>
|
|
69
|
-
|
|
69
|
+
k2NIGYptDbpRKu5vmsmuboSr7G2j4qb9eftlewub0LA=
|
|
70
70
|
</data>
|
|
71
71
|
</dict>
|
|
72
72
|
<key>Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface</key>
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
<dict>
|
|
95
95
|
<key>hash2</key>
|
|
96
96
|
<data>
|
|
97
|
-
|
|
97
|
+
k2NIGYptDbpRKu5vmsmuboSr7G2j4qb9eftlewub0LA=
|
|
98
98
|
</data>
|
|
99
99
|
</dict>
|
|
100
100
|
<key>Modules/ShortKitSDK.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface</key>
|