@shortkitsdk/react-native 0.2.11 → 0.2.14
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 +13 -1
- package/android/src/main/java/com/shortkit/reactnative/ReactCarouselOverlayHost.kt +157 -54
- package/android/src/main/java/com/shortkit/reactnative/ReactOverlayHost.kt +67 -56
- package/android/src/main/java/com/shortkit/reactnative/ReactVideoCarouselOverlayHost.kt +431 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitBridge.kt +154 -26
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedView.kt +160 -35
- package/android/src/main/java/com/shortkit/reactnative/ShortKitFeedViewManager.kt +5 -0
- package/android/src/main/java/com/shortkit/reactnative/ShortKitModule.kt +45 -10
- package/android/src/main/java/com/shortkit/reactnative/ShortKitPlayerNativeView.kt +9 -0
- package/ios/ReactCarouselOverlayHost.swift +37 -17
- package/ios/ReactOverlayHost.swift +33 -35
- package/ios/ReactVideoCarouselOverlayHost.swift +283 -0
- package/ios/ShortKitBridge.swift +78 -2
- package/ios/ShortKitFeedView.swift +24 -3
- package/ios/ShortKitModule.mm +6 -2
- package/ios/ShortKitSDK.xcframework/Info.plist +4 -4
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +2597 -389
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +69 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +69 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64/ShortKitSDK.framework/_CodeSignature/CodeResources +168 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +2597 -389
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +69 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +69 -5
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework/ios-arm64-simulator/ShortKitSDK.framework/_CodeSignature/CodeResources +168 -0
- package/ios/ShortKitSDK.xcframework.bak2/Info.plist +43 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.abi.json +31351 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.private.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Headers/ShortKitSDK-Swift.h +418 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Info.plist +16 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.abi.json +31351 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/ShortKitSDK.swiftmodule/arm64-apple-ios-simulator.swiftinterface +865 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/Modules/module.modulemap +4 -0
- package/ios/ShortKitSDK.xcframework.bak2/ios-arm64-simulator/ShortKitSDK.framework/ShortKitSDK +0 -0
- package/package.json +1 -1
- package/src/ShortKitCarouselOverlaySurface.tsx +57 -2
- package/src/ShortKitContext.ts +2 -1
- package/src/ShortKitFeed.tsx +19 -1
- package/src/ShortKitOverlaySurface.tsx +148 -41
- package/src/ShortKitPlayer.tsx +25 -3
- package/src/ShortKitProvider.tsx +4 -2
- package/src/ShortKitVideoCarouselOverlaySurface.tsx +156 -0
- package/src/index.ts +8 -1
- package/src/serialization.ts +8 -0
- package/src/specs/NativeShortKitModule.ts +31 -1
- package/src/types.ts +45 -1
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
package com.shortkit.reactnative
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Bundle
|
|
5
|
+
import android.os.Handler
|
|
6
|
+
import android.os.Looper
|
|
7
|
+
import android.widget.FrameLayout
|
|
8
|
+
import com.facebook.react.ReactApplication
|
|
9
|
+
import com.facebook.react.bridge.Arguments
|
|
10
|
+
import com.facebook.react.interfaces.fabric.ReactSurface
|
|
11
|
+
import com.facebook.react.runtime.ReactSurfaceImpl
|
|
12
|
+
import com.shortkit.sdk.ShortKitPlayer
|
|
13
|
+
import com.shortkit.sdk.model.ContentItem
|
|
14
|
+
import com.shortkit.sdk.model.PlayerState
|
|
15
|
+
import com.shortkit.sdk.model.VideoCarouselItem
|
|
16
|
+
import com.shortkit.sdk.overlay.VideoCarouselOverlay
|
|
17
|
+
import kotlinx.coroutines.CoroutineScope
|
|
18
|
+
import kotlinx.coroutines.Dispatchers
|
|
19
|
+
import kotlinx.coroutines.SupervisorJob
|
|
20
|
+
import kotlinx.coroutines.cancel
|
|
21
|
+
import kotlinx.coroutines.launch
|
|
22
|
+
import kotlinx.serialization.encodeToString
|
|
23
|
+
import kotlinx.serialization.json.Json
|
|
24
|
+
import java.util.UUID
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A [FrameLayout] that conforms to [VideoCarouselOverlay] and hosts a React Native
|
|
28
|
+
* Fabric [ReactSurface] for rendering the developer's React video carousel overlay
|
|
29
|
+
* component inside a feed cell.
|
|
30
|
+
*
|
|
31
|
+
* Pushes [VideoCarouselItem] and active video data as surface properties, and
|
|
32
|
+
* emits playback state (isActive, time, playerState, isMuted) via bridge events
|
|
33
|
+
* using the same event names as [ReactOverlayHost] (routed by [surfaceId]).
|
|
34
|
+
*
|
|
35
|
+
* Android equivalent of `react_native_sdk/ios/ReactVideoCarouselOverlayHost.swift`.
|
|
36
|
+
*/
|
|
37
|
+
class ReactVideoCarouselOverlayHost(context: Context) : FrameLayout(context), VideoCarouselOverlay {
|
|
38
|
+
|
|
39
|
+
private companion object {
|
|
40
|
+
const val TAG = "SK:VidCarouselHost"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Touch handling: this view sits below the ViewPager2 in z-order.
|
|
44
|
+
// The cell's root FrameLayout dispatches taps to this overlay when the
|
|
45
|
+
// ViewPager2 doesn't consume them (i.e. non-scroll touches).
|
|
46
|
+
|
|
47
|
+
// ------------------------------------------------------------------
|
|
48
|
+
// Configuration
|
|
49
|
+
// ------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
/** Suffix for the surface module name (e.g. "news" -> "ShortKitVideoCarouselOverlay_news"). */
|
|
52
|
+
var videoCarouselOverlayName: String = "Default"
|
|
53
|
+
|
|
54
|
+
// ------------------------------------------------------------------
|
|
55
|
+
// State
|
|
56
|
+
// ------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
private var surface: ReactSurface? = null
|
|
59
|
+
private var pendingProps: Bundle? = null
|
|
60
|
+
private var isInLayoutPass: Boolean = false
|
|
61
|
+
|
|
62
|
+
/** Unique identifier for this overlay instance, used for event routing. */
|
|
63
|
+
val surfaceId: String = UUID.randomUUID().toString()
|
|
64
|
+
|
|
65
|
+
/** Cached carouselItem JSON — updateInitProps replaces all props, doesn't merge. */
|
|
66
|
+
private var carouselItemJSON: String? = null
|
|
67
|
+
|
|
68
|
+
// Player state
|
|
69
|
+
private var player: ShortKitPlayer? = null
|
|
70
|
+
private var isActive: Boolean = false
|
|
71
|
+
private var cachedPlayerState: String = "idle"
|
|
72
|
+
private var cachedIsMuted: Boolean = true
|
|
73
|
+
private var cachedCurrentTime: Double = 0.0
|
|
74
|
+
private var cachedDuration: Double = 0.0
|
|
75
|
+
private var cachedBuffered: Double = 0.0
|
|
76
|
+
private var timeDirty: Boolean = false
|
|
77
|
+
private val handler = Handler(Looper.getMainLooper())
|
|
78
|
+
private var timeCoalesceRunnable: Runnable? = null
|
|
79
|
+
private var flowScope: CoroutineScope? = null
|
|
80
|
+
|
|
81
|
+
// ------------------------------------------------------------------
|
|
82
|
+
// Fabric layout workaround
|
|
83
|
+
// ------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
private val layoutHandler = Handler(Looper.getMainLooper())
|
|
86
|
+
|
|
87
|
+
private val layoutRunnable = Runnable {
|
|
88
|
+
measure(
|
|
89
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
90
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
91
|
+
)
|
|
92
|
+
layout(left, top, right, bottom)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
override fun requestLayout() {
|
|
96
|
+
super.requestLayout()
|
|
97
|
+
if (!isInLayoutPass) {
|
|
98
|
+
@Suppress("UNNECESSARY_SAFE_CALL")
|
|
99
|
+
layoutHandler?.post(layoutRunnable)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ------------------------------------------------------------------
|
|
104
|
+
// Init
|
|
105
|
+
// ------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
init {
|
|
108
|
+
setBackgroundColor(android.graphics.Color.TRANSPARENT)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ------------------------------------------------------------------
|
|
112
|
+
// Surface warmup
|
|
113
|
+
// ------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
fun prepareSurface() {
|
|
116
|
+
createSurfaceIfNeeded()
|
|
117
|
+
|
|
118
|
+
val displayW = context.resources.displayMetrics.widthPixels
|
|
119
|
+
val displayH = context.resources.displayMetrics.heightPixels
|
|
120
|
+
val wSpec = MeasureSpec.makeMeasureSpec(displayW, MeasureSpec.EXACTLY)
|
|
121
|
+
val hSpec = MeasureSpec.makeMeasureSpec(displayH, MeasureSpec.EXACTLY)
|
|
122
|
+
measure(wSpec, hSpec)
|
|
123
|
+
layout(0, 0, displayW, displayH)
|
|
124
|
+
measureAndLayoutSurfaceView(displayW, displayH)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ------------------------------------------------------------------
|
|
128
|
+
// VideoCarouselOverlay
|
|
129
|
+
// ------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
override fun configure(item: VideoCarouselItem) {
|
|
132
|
+
isActive = false
|
|
133
|
+
timeDirty = false
|
|
134
|
+
stopTimeCoalescing()
|
|
135
|
+
cachedCurrentTime = 0.0
|
|
136
|
+
cachedDuration = 0.0
|
|
137
|
+
cachedBuffered = 0.0
|
|
138
|
+
cachedPlayerState = "idle"
|
|
139
|
+
|
|
140
|
+
val json = Json.encodeToString(item)
|
|
141
|
+
carouselItemJSON = json
|
|
142
|
+
val bundle = Bundle().apply {
|
|
143
|
+
putString("surfaceId", surfaceId)
|
|
144
|
+
putString("carouselItem", json)
|
|
145
|
+
putBoolean("isActive", false)
|
|
146
|
+
putString("playerState", "idle")
|
|
147
|
+
putBoolean("isMuted", cachedIsMuted)
|
|
148
|
+
if (item.videos.isNotEmpty()) {
|
|
149
|
+
putString("activeVideo", ShortKitBridge.serializeContentItemToJSON(item.videos.first()))
|
|
150
|
+
putInt("activeVideoIndex", 0)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
applySurfaceProps(bundle)
|
|
154
|
+
|
|
155
|
+
// Pre-size the surface view
|
|
156
|
+
val parentView = parent as? android.view.View
|
|
157
|
+
val w = if (width > 0) width
|
|
158
|
+
else if (parentView != null && parentView.width > 0) parentView.width
|
|
159
|
+
else context.resources.displayMetrics.widthPixels
|
|
160
|
+
val h = if (height > 0) height
|
|
161
|
+
else if (parentView != null && parentView.height > 0) parentView.height
|
|
162
|
+
else context.resources.displayMetrics.heightPixels
|
|
163
|
+
measureAndLayoutSurfaceView(w, h)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override fun updateActiveVideo(index: Int, item: ContentItem) {
|
|
167
|
+
// Only emit when active — matches ReactOverlayHost pattern.
|
|
168
|
+
// During initial setup, configure() sets the first video via surface props.
|
|
169
|
+
if (!isActive) return
|
|
170
|
+
|
|
171
|
+
val params = Arguments.createMap().apply {
|
|
172
|
+
putString("surfaceId", surfaceId)
|
|
173
|
+
putString("activeVideo", ShortKitBridge.serializeContentItemToJSON(item))
|
|
174
|
+
putInt("activeVideoIndex", index)
|
|
175
|
+
}
|
|
176
|
+
ShortKitBridge.shared?.emitEvent("onVideoCarouselActiveVideoChanged", params)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
override fun resetState() {
|
|
180
|
+
// Don't clear surface props — configure() will overwrite.
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
override fun attach(player: ShortKitPlayer) {
|
|
184
|
+
this.player = player
|
|
185
|
+
resubscribeToPlayer(player)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
override fun activatePlayback() {
|
|
189
|
+
isActive = true
|
|
190
|
+
startTimeCoalescing()
|
|
191
|
+
|
|
192
|
+
handler.post {
|
|
193
|
+
if (isActive) {
|
|
194
|
+
emitFullState()
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
override fun deactivatePlayback() {
|
|
200
|
+
isActive = false
|
|
201
|
+
timeDirty = false
|
|
202
|
+
stopTimeCoalescing()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ------------------------------------------------------------------
|
|
206
|
+
// Player Subscriptions
|
|
207
|
+
// ------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
private fun resubscribeToPlayer(player: ShortKitPlayer) {
|
|
210
|
+
flowScope?.cancel()
|
|
211
|
+
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
212
|
+
flowScope = scope
|
|
213
|
+
|
|
214
|
+
scope.launch {
|
|
215
|
+
player.playerState.collect { state ->
|
|
216
|
+
cachedPlayerState = ShortKitBridge.playerStateString(state)
|
|
217
|
+
if (isActive) {
|
|
218
|
+
val params = Arguments.createMap().apply {
|
|
219
|
+
putString("surfaceId", surfaceId)
|
|
220
|
+
putString("playerState", cachedPlayerState)
|
|
221
|
+
}
|
|
222
|
+
ShortKitBridge.shared?.emitEvent("onOverlayPlayerStateChanged", params)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
scope.launch {
|
|
228
|
+
player.isMuted.collect { muted ->
|
|
229
|
+
cachedIsMuted = muted
|
|
230
|
+
if (isActive) {
|
|
231
|
+
val params = Arguments.createMap().apply {
|
|
232
|
+
putString("surfaceId", surfaceId)
|
|
233
|
+
putBoolean("isMuted", cachedIsMuted)
|
|
234
|
+
}
|
|
235
|
+
ShortKitBridge.shared?.emitEvent("onOverlayMutedChanged", params)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
scope.launch {
|
|
241
|
+
player.time.collect { time ->
|
|
242
|
+
cachedCurrentTime = time.currentMs / 1000.0
|
|
243
|
+
cachedDuration = time.durationMs / 1000.0
|
|
244
|
+
cachedBuffered = time.bufferedMs / 1000.0
|
|
245
|
+
if (isActive) {
|
|
246
|
+
timeDirty = true
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ------------------------------------------------------------------
|
|
253
|
+
// Time Coalescing (250ms)
|
|
254
|
+
// ------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
private fun startTimeCoalescing() {
|
|
257
|
+
stopTimeCoalescing()
|
|
258
|
+
val runnable = object : Runnable {
|
|
259
|
+
override fun run() {
|
|
260
|
+
if (isActive && timeDirty && cachedPlayerState != "seeking") {
|
|
261
|
+
emitTimeUpdate()
|
|
262
|
+
timeDirty = false
|
|
263
|
+
}
|
|
264
|
+
if (isActive) {
|
|
265
|
+
handler.postDelayed(this, 250)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
timeCoalesceRunnable = runnable
|
|
270
|
+
handler.postDelayed(runnable, 250)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private fun stopTimeCoalescing() {
|
|
274
|
+
timeCoalesceRunnable?.let { handler.removeCallbacks(it) }
|
|
275
|
+
timeCoalesceRunnable = null
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private fun emitTimeUpdate() {
|
|
279
|
+
val params = Arguments.createMap().apply {
|
|
280
|
+
putString("surfaceId", surfaceId)
|
|
281
|
+
putDouble("current", cachedCurrentTime)
|
|
282
|
+
putDouble("duration", cachedDuration)
|
|
283
|
+
putDouble("buffered", cachedBuffered)
|
|
284
|
+
}
|
|
285
|
+
ShortKitBridge.shared?.emitEvent("onOverlayTimeUpdate", params)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ------------------------------------------------------------------
|
|
289
|
+
// Full State Emission
|
|
290
|
+
// ------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
private fun emitFullState() {
|
|
293
|
+
val bridge = ShortKitBridge.shared ?: return
|
|
294
|
+
|
|
295
|
+
val params = Arguments.createMap().apply {
|
|
296
|
+
putString("surfaceId", surfaceId)
|
|
297
|
+
putBoolean("isActive", true)
|
|
298
|
+
putString("playerState", cachedPlayerState)
|
|
299
|
+
putBoolean("isMuted", cachedIsMuted)
|
|
300
|
+
putDouble("playbackRate", 1.0)
|
|
301
|
+
putBoolean("captionsEnabled", false)
|
|
302
|
+
putNull("activeCue")
|
|
303
|
+
putNull("feedScrollPhase")
|
|
304
|
+
}
|
|
305
|
+
bridge.emitEvent("onOverlayFullState", params)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ------------------------------------------------------------------
|
|
309
|
+
// Surface Creation
|
|
310
|
+
// ------------------------------------------------------------------
|
|
311
|
+
|
|
312
|
+
/** Apply props to the surface, handling all lifecycle states. */
|
|
313
|
+
private fun applySurfaceProps(bundle: Bundle) {
|
|
314
|
+
val s = surface
|
|
315
|
+
if (s != null && s.isRunning) {
|
|
316
|
+
(s as? ReactSurfaceImpl)?.updateInitProps(bundle)
|
|
317
|
+
} else if (s != null && !s.isRunning) {
|
|
318
|
+
(s as? ReactSurfaceImpl)?.updateInitProps(bundle)
|
|
319
|
+
s.start()
|
|
320
|
+
} else {
|
|
321
|
+
pendingProps = bundle
|
|
322
|
+
createSurfaceIfNeeded()
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
|
327
|
+
super.onSizeChanged(w, h, oldw, oldh)
|
|
328
|
+
if (w > 0 && h > 0) {
|
|
329
|
+
measureAndLayoutSurfaceView(w, h)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
334
|
+
isInLayoutPass = true
|
|
335
|
+
try {
|
|
336
|
+
super.onLayout(changed, left, top, right, bottom)
|
|
337
|
+
val w = right - left
|
|
338
|
+
val h = bottom - top
|
|
339
|
+
if (w > 0 && h > 0) {
|
|
340
|
+
measureAndLayoutSurfaceView(w, h)
|
|
341
|
+
} else {
|
|
342
|
+
val parentView = parent as? android.view.View
|
|
343
|
+
val pw = parentView?.width ?: 0
|
|
344
|
+
val ph = parentView?.height ?: 0
|
|
345
|
+
if (pw > 0 && ph > 0) {
|
|
346
|
+
measureAndLayoutSurfaceView(pw, ph)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} finally {
|
|
350
|
+
isInLayoutPass = false
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private fun measureAndLayoutSurfaceView(w: Int, h: Int) {
|
|
355
|
+
val sv = surface?.view ?: return
|
|
356
|
+
if (sv.width == w && sv.height == h) return
|
|
357
|
+
val wSpec = android.view.View.MeasureSpec.makeMeasureSpec(w, android.view.View.MeasureSpec.EXACTLY)
|
|
358
|
+
val hSpec = android.view.View.MeasureSpec.makeMeasureSpec(h, android.view.View.MeasureSpec.EXACTLY)
|
|
359
|
+
sv.measure(wSpec, hSpec)
|
|
360
|
+
sv.layout(0, 0, w, h)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
override fun onAttachedToWindow() {
|
|
364
|
+
super.onAttachedToWindow()
|
|
365
|
+
|
|
366
|
+
// Re-subscribe to player flows if we were detached and reattached
|
|
367
|
+
if (flowScope == null) {
|
|
368
|
+
player?.let { resubscribeToPlayer(it) }
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
val parentView = parent as? android.view.View
|
|
372
|
+
if (parentView != null && parentView.width > 0 && parentView.height > 0 && (width == 0 || height == 0)) {
|
|
373
|
+
val wSpec = MeasureSpec.makeMeasureSpec(parentView.width, MeasureSpec.EXACTLY)
|
|
374
|
+
val hSpec = MeasureSpec.makeMeasureSpec(parentView.height, MeasureSpec.EXACTLY)
|
|
375
|
+
measure(wSpec, hSpec)
|
|
376
|
+
layout(0, 0, parentView.width, parentView.height)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private fun createSurfaceIfNeeded() {
|
|
381
|
+
if (surface != null) return
|
|
382
|
+
|
|
383
|
+
val reactHost = (context.applicationContext as? ReactApplication)?.reactHost
|
|
384
|
+
if (reactHost == null) {
|
|
385
|
+
android.util.Log.e(TAG, "createSurface FAILED: reactHost is null")
|
|
386
|
+
return
|
|
387
|
+
}
|
|
388
|
+
val moduleName = "ShortKitVideoCarouselOverlay_$videoCarouselOverlayName"
|
|
389
|
+
|
|
390
|
+
val initialProps = pendingProps
|
|
391
|
+
pendingProps = null
|
|
392
|
+
|
|
393
|
+
val newSurface = reactHost.createSurface(context, moduleName, initialProps)
|
|
394
|
+
surface = newSurface
|
|
395
|
+
|
|
396
|
+
newSurface.view?.let { surfaceView ->
|
|
397
|
+
surfaceView.layoutParams = LayoutParams(
|
|
398
|
+
LayoutParams.MATCH_PARENT,
|
|
399
|
+
LayoutParams.MATCH_PARENT
|
|
400
|
+
)
|
|
401
|
+
addView(surfaceView)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Only start if we have item data
|
|
405
|
+
if (initialProps != null) {
|
|
406
|
+
newSurface.start()
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
val parentView = parent as? android.view.View
|
|
410
|
+
val w = if (width > 0) width else parentView?.width ?: 0
|
|
411
|
+
val h = if (height > 0) height else parentView?.height ?: 0
|
|
412
|
+
if (w > 0 && h > 0) {
|
|
413
|
+
measureAndLayoutSurfaceView(w, h)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ------------------------------------------------------------------
|
|
418
|
+
// Cleanup
|
|
419
|
+
// ------------------------------------------------------------------
|
|
420
|
+
|
|
421
|
+
override fun onDetachedFromWindow() {
|
|
422
|
+
super.onDetachedFromWindow()
|
|
423
|
+
flowScope?.cancel()
|
|
424
|
+
flowScope = null
|
|
425
|
+
isActive = false
|
|
426
|
+
stopTimeCoalescing()
|
|
427
|
+
if (surface?.isRunning == true) {
|
|
428
|
+
surface?.stop()
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|