react-native-spalla-player 0.13.3 → 1.0.1
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/LICENSE +2 -3
- package/SpallaPlayer.podspec +22 -0
- package/android/build.gradle +22 -58
- package/android/gradle.properties +5 -5
- package/android/src/main/AndroidManifest.xml +1 -2
- package/android/src/main/java/com/spallaplayer/RNSpallaPlayerEvent.kt +19 -0
- package/android/src/main/java/com/spallaplayer/SpallaPlayerModule.kt +64 -150
- package/android/src/main/java/com/spallaplayer/SpallaPlayerPackage.kt +15 -3
- package/android/src/main/java/com/spallaplayer/SpallaPlayerViewManager.kt +164 -107
- package/ios/SpallaPlayerModule.h +17 -0
- package/ios/SpallaPlayerModule.mm +59 -0
- package/ios/SpallaPlayerView.h +14 -0
- package/ios/SpallaPlayerView.mm +158 -0
- package/ios/SpallaPlayerWrapper.swift +12 -8
- package/lib/module/NativeSpallaPlayerModule.js +5 -0
- package/lib/module/NativeSpallaPlayerModule.js.map +1 -0
- package/lib/module/SpallaPlayerViewNativeComponent.js +11 -0
- package/lib/module/SpallaPlayerViewNativeComponent.js.map +1 -0
- package/lib/module/index.js +48 -55
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeSpallaPlayerModule.d.ts +13 -0
- package/lib/typescript/src/NativeSpallaPlayerModule.d.ts.map +1 -0
- package/lib/typescript/src/SpallaPlayerViewNativeComponent.d.ts +33 -0
- package/lib/typescript/src/SpallaPlayerViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/components/CastButton.d.ts.map +1 -0
- package/lib/typescript/{module/src → src}/index.d.ts +9 -15
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +93 -103
- package/src/NativeSpallaPlayerModule.ts +14 -0
- package/src/SpallaPlayerViewNativeComponent.ts +61 -0
- package/src/index.tsx +71 -82
- package/android/src/main/AndroidManifestNew.xml +0 -2
- package/android/src/main/java/com/spallaplayer/SpallaPlayerContainerView.kt +0 -36
- package/ios/RNSpallaPlayer.m +0 -102
- package/ios/RNSpallaPlayer.swift +0 -11
- package/ios/SpallaPlayer-Bridging-Header.h +0 -1
- package/lib/commonjs/components/CastButton.js +0 -49
- package/lib/commonjs/components/CastButton.js.map +0 -1
- package/lib/commonjs/index.js +0 -81
- package/lib/commonjs/index.js.map +0 -1
- package/lib/typescript/commonjs/package.json +0 -1
- package/lib/typescript/commonjs/src/components/CastButton.d.ts.map +0 -1
- package/lib/typescript/commonjs/src/index.d.ts +0 -61
- package/lib/typescript/commonjs/src/index.d.ts.map +0 -1
- package/lib/typescript/module/src/components/CastButton.d.ts +0 -13
- package/lib/typescript/module/src/components/CastButton.d.ts.map +0 -1
- package/lib/typescript/module/src/index.d.ts.map +0 -1
- package/react-native-spalla-player.podspec +0 -44
- /package/lib/{typescript/module → module}/package.json +0 -0
- /package/lib/typescript/{commonjs/src → src}/components/CastButton.d.ts +0 -0
|
@@ -4,145 +4,210 @@ import android.util.Log
|
|
|
4
4
|
import android.view.View
|
|
5
5
|
import com.facebook.react.bridge.Arguments
|
|
6
6
|
import com.facebook.react.bridge.ReactContext
|
|
7
|
+
import com.facebook.react.bridge.ReadableArray
|
|
7
8
|
import com.facebook.react.bridge.WritableMap
|
|
9
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
8
10
|
import com.facebook.react.uimanager.ViewGroupManager
|
|
9
11
|
import com.facebook.react.uimanager.ThemedReactContext
|
|
12
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
13
|
+
import com.facebook.react.uimanager.ViewManagerDelegate
|
|
10
14
|
import com.facebook.react.uimanager.annotations.ReactProp
|
|
11
|
-
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
12
15
|
import com.spalla.sdk.android.core.player.entities.SpallaPlayerEvent
|
|
13
16
|
import com.spalla.sdk.android.core.player.entities.SpallaPlayerEvent.*
|
|
14
17
|
import com.spalla.sdk.android.core.player.listeners.SpallaPlayerFullScreenListener
|
|
15
18
|
import com.spalla.sdk.android.core.player.listeners.SpallaPlayerListener
|
|
16
19
|
import com.spalla.sdk.android.core.player.view.SpallaPlayerView
|
|
17
20
|
import java.util.Timer
|
|
18
|
-
import java.util.TimerTask
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
// These imports will work because Codegen runs for Fabric builds.
|
|
23
|
+
// Your IDE will show errors on these lines until you build the project.
|
|
24
|
+
import com.facebook.react.viewmanagers.SpallaPlayerViewManagerInterface
|
|
25
|
+
import com.facebook.react.viewmanagers.SpallaPlayerViewManagerDelegate
|
|
26
|
+
|
|
27
|
+
// Implement the Codegen interface for Fabric support
|
|
28
|
+
@ReactModule(name = SpallaPlayerViewManager.REACT_CLASS)
|
|
29
|
+
class SpallaPlayerViewManager() : ViewGroupManager<SpallaPlayerView>(),
|
|
30
|
+
SpallaPlayerViewManagerInterface<SpallaPlayerView>, // Fabric interface
|
|
21
31
|
SpallaPlayerListener, SpallaPlayerFullScreenListener {
|
|
32
|
+
|
|
33
|
+
companion object {
|
|
34
|
+
const val REACT_CLASS = "SpallaPlayerView"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The delegate is required for Fabric to handle props and commands.
|
|
38
|
+
private val mDelegate = SpallaPlayerViewManagerDelegate(this)
|
|
39
|
+
|
|
40
|
+
// This method is required by the Fabric architecture.
|
|
41
|
+
override fun getDelegate(): ViewManagerDelegate<SpallaPlayerView> = mDelegate
|
|
42
|
+
|
|
22
43
|
private var _playerView: SpallaPlayerView? = null
|
|
23
44
|
private var _reactContext: ReactContext? = null
|
|
24
|
-
private var _container:
|
|
25
|
-
|
|
45
|
+
//private var _container: SpallaPlayerView? = null
|
|
26
46
|
private var contentId: String? = null
|
|
27
47
|
private var startTime: Double? = null
|
|
28
48
|
private var subtitle: String? = null
|
|
29
49
|
private var loadTimer: Timer? = null
|
|
30
50
|
private var playbackRate: Double = 1.0
|
|
31
51
|
private var hideUI: Boolean? = null
|
|
52
|
+
private var _viewTag: Int = -1
|
|
32
53
|
|
|
33
|
-
override fun getName() =
|
|
54
|
+
override fun getName() = REACT_CLASS
|
|
34
55
|
|
|
35
|
-
override fun createViewInstance(context: ThemedReactContext):
|
|
56
|
+
override fun createViewInstance(context: ThemedReactContext): SpallaPlayerView {
|
|
36
57
|
_reactContext = context
|
|
37
|
-
val container = SpallaPlayerContainerView(context)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
//val container = SpallaPlayerContainerView(context)
|
|
59
|
+
val player = SpallaPlayerView(context)
|
|
60
|
+
|
|
61
|
+
// Defer initialization until view is attached to window
|
|
62
|
+
player.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
|
63
|
+
override fun onViewAttachedToWindow(view: View) {
|
|
64
|
+
try {
|
|
65
|
+
_playerView = player
|
|
66
|
+
_playerView?.registerPlayerListener(this@SpallaPlayerViewManager)
|
|
67
|
+
_playerView?.registerFullScreenListener(this@SpallaPlayerViewManager)
|
|
68
|
+
// Remove listener to avoid memory leaks
|
|
69
|
+
view.removeOnAttachStateChangeListener(this)
|
|
70
|
+
|
|
71
|
+
checkAndLoadPlayer(player)
|
|
72
|
+
} catch (e: Exception) {
|
|
73
|
+
Log.e("SpallaPlayerViewManager", "Error during view attachment", e)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
45
76
|
|
|
46
|
-
|
|
47
|
-
|
|
77
|
+
override fun onViewDetachedFromWindow(view: View) {
|
|
78
|
+
_playerView?.pause()
|
|
48
79
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"bubbled" to "onPlayerEvent"
|
|
52
|
-
)
|
|
53
|
-
)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
54
82
|
|
|
55
|
-
|
|
83
|
+
//this._container = container
|
|
84
|
+
//return container
|
|
85
|
+
return player
|
|
56
86
|
}
|
|
57
87
|
|
|
58
|
-
override fun onDropViewInstance(view:
|
|
88
|
+
override fun onDropViewInstance(view: SpallaPlayerView) {
|
|
59
89
|
Log.v("RNSpallaPlayerManager", "onDropViewInstance")
|
|
60
|
-
|
|
61
90
|
try {
|
|
62
|
-
view.
|
|
91
|
+
view.onDestroy()
|
|
63
92
|
} catch (e: Exception) {
|
|
64
93
|
e.printStackTrace()
|
|
65
94
|
}
|
|
66
95
|
super.onDropViewInstance(view)
|
|
67
96
|
}
|
|
68
97
|
|
|
69
|
-
|
|
70
|
-
parent.addView(child, index)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
override fun removeViewAt(parent: SpallaPlayerContainerView, index: Int) {
|
|
74
|
-
parent.removeViewAt(index)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
override fun getChildCount(parent: SpallaPlayerContainerView): Int {
|
|
78
|
-
return parent.childCount
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
override fun getChildAt(parent: SpallaPlayerContainerView, index: Int): View {
|
|
82
|
-
return parent.getChildAt(index)
|
|
83
|
-
}
|
|
84
|
-
|
|
98
|
+
// --- Prop Setters ---
|
|
85
99
|
@ReactProp(name = "contentId")
|
|
86
|
-
fun setContentId(view:
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
override fun setContentId(view: SpallaPlayerView, value: String?) {
|
|
101
|
+
_viewTag = view.tag as? Int ?: -1
|
|
102
|
+
if (value != null) {
|
|
103
|
+
this.contentId = value
|
|
104
|
+
//add a hacky delay here, so if content and starttime are changed at same time,
|
|
105
|
+
//start time is set first
|
|
106
|
+
view.postDelayed({
|
|
107
|
+
checkAndLoadPlayer(view)
|
|
108
|
+
}, 100L)
|
|
109
|
+
}
|
|
89
110
|
}
|
|
90
111
|
|
|
91
112
|
@ReactProp(name = "muted")
|
|
92
|
-
fun setMuted(view:
|
|
93
|
-
_playerView?.setMuted(
|
|
113
|
+
override fun setMuted(view: SpallaPlayerView, value: Boolean) {
|
|
114
|
+
_playerView?.setMuted(value)
|
|
94
115
|
}
|
|
95
116
|
|
|
96
117
|
@ReactProp(name = "startTime")
|
|
97
|
-
fun setStartTime(view:
|
|
98
|
-
this.startTime =
|
|
118
|
+
override fun setStartTime(view: SpallaPlayerView, value: Double) {
|
|
119
|
+
this.startTime = value
|
|
99
120
|
//checkAndLoadPlayer(view)
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
@ReactProp(name = "subtitle")
|
|
103
|
-
fun setSubtitle(view:
|
|
104
|
-
this.subtitle =
|
|
124
|
+
override fun setSubtitle(view: SpallaPlayerView, value: String?) {
|
|
125
|
+
this.subtitle = value
|
|
105
126
|
_playerView?.selectSubtitle(subtitle)
|
|
106
127
|
}
|
|
107
128
|
|
|
108
129
|
@ReactProp(name = "playbackRate")
|
|
109
|
-
fun setPlaybackRate(view:
|
|
110
|
-
if (
|
|
111
|
-
this.playbackRate =
|
|
130
|
+
override fun setPlaybackRate(view: SpallaPlayerView, value: Float) {
|
|
131
|
+
if (value > 0) {
|
|
132
|
+
this.playbackRate = value.toDouble()
|
|
112
133
|
_playerView?.selectPlaybackRate(playbackRate)
|
|
113
134
|
}
|
|
114
135
|
}
|
|
115
136
|
|
|
116
137
|
@ReactProp(name = "hideUI")
|
|
117
|
-
fun
|
|
118
|
-
this.hideUI =
|
|
119
|
-
checkAndLoadPlayer(view)
|
|
138
|
+
override fun setHideUI(view: SpallaPlayerView, value: Boolean) {
|
|
139
|
+
this.hideUI = value
|
|
140
|
+
//checkAndLoadPlayer(view)
|
|
120
141
|
}
|
|
121
142
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
143
|
+
override fun play(view: SpallaPlayerView?) {
|
|
144
|
+
view?.play()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override fun pause(view: SpallaPlayerView?) {
|
|
148
|
+
view?.pause()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
override fun seekTo(
|
|
152
|
+
view: SpallaPlayerView?,
|
|
153
|
+
time: Double
|
|
154
|
+
) {
|
|
155
|
+
view?.seekTo(time)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
override fun selectSubtitle(
|
|
159
|
+
view: SpallaPlayerView?,
|
|
160
|
+
subtitle: String?
|
|
161
|
+
) {
|
|
162
|
+
view?.selectSubtitle(subtitle)
|
|
126
163
|
}
|
|
127
164
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
165
|
+
override fun selectPlaybackRate(
|
|
166
|
+
view: SpallaPlayerView?,
|
|
167
|
+
rate: Float
|
|
168
|
+
) {
|
|
169
|
+
view?.selectPlaybackRate(rate.toDouble())
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
override fun unmount(view: SpallaPlayerView?) {
|
|
173
|
+
view?.pause()
|
|
174
|
+
view?.onDestroy()
|
|
175
|
+
}
|
|
131
176
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
loadTimer = null
|
|
138
|
-
// Call view.load with the required properties
|
|
139
|
-
view.load(contentId!!, true, startTime!!, subtitle = subtitle, hideUI!!)
|
|
177
|
+
private fun checkAndLoadPlayer(view: SpallaPlayerView) {
|
|
178
|
+
try {
|
|
179
|
+
// Only load if view is attached to window and all required props are set
|
|
180
|
+
if (contentId != null && startTime != null && hideUI != null && view.isAttachedToWindow) {
|
|
181
|
+
view.load(contentId!!, true, startTime!!, subtitle, hideUI!!)
|
|
140
182
|
}
|
|
141
|
-
}
|
|
183
|
+
} catch (e: Exception) {
|
|
184
|
+
Log.e("SpallaPlayerViewManager", "Error loading player", e)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// --- Command Handling ---
|
|
189
|
+
override fun receiveCommand(
|
|
190
|
+
root: SpallaPlayerView,
|
|
191
|
+
commandId: String,
|
|
192
|
+
args: ReadableArray?
|
|
193
|
+
) {
|
|
194
|
+
mDelegate.receiveCommand(root, commandId, args)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// --- Event Handling ---
|
|
198
|
+
override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> {
|
|
199
|
+
return mutableMapOf(
|
|
200
|
+
"onPlayerEvent" to mapOf(
|
|
201
|
+
"phasedRegistrationNames" to mapOf(
|
|
202
|
+
"bubbled" to "onPlayerEvent"
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
)
|
|
142
206
|
}
|
|
143
207
|
|
|
144
208
|
override fun onEvent(event: SpallaPlayerEvent) {
|
|
145
209
|
val map: WritableMap = Arguments.createMap()
|
|
210
|
+
val eventName = "onPlayerEvent"
|
|
146
211
|
|
|
147
212
|
Log.v("RNSpallaPlayerManager", "onEvent: $event")
|
|
148
213
|
when (event) {
|
|
@@ -150,13 +215,11 @@ class RNSpallaPlayerManager() : ViewGroupManager<SpallaPlayerContainerView>(),
|
|
|
150
215
|
map.putString("event", "durationUpdate")
|
|
151
216
|
map.putDouble("duration", event.duration)
|
|
152
217
|
}
|
|
153
|
-
|
|
154
218
|
Ended -> map.putString("event", "ended")
|
|
155
219
|
is Error -> {
|
|
156
220
|
map.putString("event", "error")
|
|
157
221
|
map.putString("message", event.message)
|
|
158
222
|
}
|
|
159
|
-
|
|
160
223
|
Pause -> map.putString("event", "pause")
|
|
161
224
|
Play -> map.putString("event", "play")
|
|
162
225
|
Playing -> map.putString("event", "playing")
|
|
@@ -164,69 +227,63 @@ class RNSpallaPlayerManager() : ViewGroupManager<SpallaPlayerContainerView>(),
|
|
|
164
227
|
map.putString("event", "timeUpdate")
|
|
165
228
|
map.putDouble("time", event.currentTime)
|
|
166
229
|
}
|
|
167
|
-
|
|
168
230
|
Waiting -> map.putString("event", "buffering")
|
|
169
231
|
is SubtitlesAvailable -> {
|
|
170
232
|
map.putString("event", "subtitlesAvailable")
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
event.subtitles.forEach {
|
|
174
|
-
subs.pushString(it)
|
|
175
|
-
}
|
|
233
|
+
val subs = Arguments.createArray()
|
|
234
|
+
event.subtitles.forEach { subs.pushString(it) }
|
|
176
235
|
map.putArray("subtitles", subs)
|
|
177
236
|
}
|
|
178
|
-
|
|
179
237
|
is SubtitleSelected -> {
|
|
180
238
|
map.putString("event", "subtitleSelected")
|
|
181
239
|
map.putString("subtitle", event.subtitle)
|
|
182
240
|
}
|
|
183
|
-
|
|
184
241
|
is MetadataLoaded -> {
|
|
185
242
|
map.putString("event", "metadataLoaded")
|
|
186
243
|
map.putBoolean("isLive", event.metadata.isLive)
|
|
187
244
|
map.putDouble("duration", event.metadata.duration)
|
|
188
|
-
|
|
189
|
-
// make sure current playback rate is applied if set initially
|
|
190
245
|
_playerView?.selectPlaybackRate(playbackRate)
|
|
191
246
|
}
|
|
192
|
-
|
|
193
247
|
is PlaybackRateSelected -> {
|
|
194
248
|
map.putString("event", "playbackRateSelected")
|
|
195
249
|
map.putDouble("rate", event.rate)
|
|
196
250
|
}
|
|
197
|
-
|
|
198
251
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
252
|
+
|
|
253
|
+
_playerView?.let { view ->
|
|
254
|
+
_reactContext?.let { reactContext ->
|
|
255
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
256
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id)
|
|
257
|
+
eventDispatcher?.dispatchEvent(
|
|
258
|
+
RNSpallaPlayerEvent(surfaceId, view.id, eventName, map)
|
|
259
|
+
)
|
|
260
|
+
}
|
|
205
261
|
}
|
|
206
262
|
}
|
|
207
263
|
|
|
208
264
|
override fun onEnterFullScreen() {
|
|
209
|
-
Log.v("SpallaPlayerViewManager", "onEnterFullScreen")
|
|
210
265
|
val map: WritableMap = Arguments.createMap()
|
|
211
266
|
map.putString("event", "enterFullScreen")
|
|
212
|
-
|
|
213
|
-
_reactContext?.getJSModule(RCTEventEmitter::class.java)?.receiveEvent(
|
|
214
|
-
container.id,
|
|
215
|
-
"onPlayerEvent",
|
|
216
|
-
map
|
|
217
|
-
)
|
|
218
|
-
}
|
|
267
|
+
dispatchEvent("onPlayerEvent", map)
|
|
219
268
|
}
|
|
220
269
|
|
|
221
270
|
override fun onExitFullScreen() {
|
|
222
271
|
val map: WritableMap = Arguments.createMap()
|
|
223
272
|
map.putString("event", "exitFullScreen")
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
273
|
+
dispatchEvent("onPlayerEvent", map)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private fun dispatchEvent(eventName: String, eventData: WritableMap) {
|
|
277
|
+
_playerView?.let { view ->
|
|
278
|
+
_reactContext?.let { reactContext ->
|
|
279
|
+
if (_viewTag != -1) {
|
|
280
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
281
|
+
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id)
|
|
282
|
+
eventDispatcher?.dispatchEvent(
|
|
283
|
+
RNSpallaPlayerEvent(surfaceId, _viewTag, eventName, eventData)
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
230
287
|
}
|
|
231
288
|
}
|
|
232
289
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// SpallaPlayerModule.h
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Rogerio Shimizu on 12/19/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import <Foundation/Foundation.h>
|
|
9
|
+
#import <SpallaPlayerViewSpec/SpallaPlayerViewSpec.h>
|
|
10
|
+
|
|
11
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
12
|
+
|
|
13
|
+
@interface SpallaPlayerModule : NSObject <NativeSpallaPlayerModuleSpec>
|
|
14
|
+
|
|
15
|
+
@end
|
|
16
|
+
|
|
17
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// SpallaPlayerModule.mm
|
|
3
|
+
// Pods
|
|
4
|
+
//
|
|
5
|
+
// Created by Rogerio Shimizu on 12/19/25.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#import "SpallaPlayerModule.h"
|
|
9
|
+
#import "SpallaPlayer-Swift.h"
|
|
10
|
+
|
|
11
|
+
@interface SpallaPlayerModule ()
|
|
12
|
+
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
@implementation SpallaPlayerModule
|
|
16
|
+
|
|
17
|
+
+ (NSString *)moduleName
|
|
18
|
+
{
|
|
19
|
+
return @"SpallaPlayerModule";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
23
|
+
return std::make_shared<facebook::react::NativeSpallaPlayerModuleSpecJSI>(params);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
- (void)initialize:(nonnull NSString *)token applicationId:(nonnull NSString *)applicationId {
|
|
27
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
28
|
+
[SpallaPlayerWrapper initializeWithToken: token applicationId: applicationId];
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (void)pause:(NSInteger)tag {
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)play:(NSInteger)tag {
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (void)seekTo:(NSInteger)tag time:(double)time {
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (void)selectPlaybackRate:(NSInteger)tag rate:(double)rate {
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)selectSubtitle:(NSInteger)tag subtitle:(NSString * _Nullable)subtitle {
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
- (void)unmount:(NSInteger)tag {
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@end
|
|
58
|
+
|
|
59
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#import <React/RCTViewComponentView.h>
|
|
2
|
+
#import <UIKit/UIKit.h>
|
|
3
|
+
|
|
4
|
+
#ifndef SpallaPlayerViewNativeComponent_h
|
|
5
|
+
#define SpallaPlayerViewNativeComponent_h
|
|
6
|
+
|
|
7
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
8
|
+
|
|
9
|
+
@interface SpallaPlayerView : RCTViewComponentView
|
|
10
|
+
@end
|
|
11
|
+
|
|
12
|
+
NS_ASSUME_NONNULL_END
|
|
13
|
+
|
|
14
|
+
#endif /* SpallaPlayerViewNativeComponent_h */
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#import "SpallaPlayerView.h"
|
|
2
|
+
|
|
3
|
+
#import <react/renderer/components/SpallaPlayerViewSpec/ComponentDescriptors.h>
|
|
4
|
+
#import <react/renderer/components/SpallaPlayerViewSpec/EventEmitters.h>
|
|
5
|
+
#import <react/renderer/components/SpallaPlayerViewSpec/Props.h>
|
|
6
|
+
#import <react/renderer/components/SpallaPlayerViewSpec/RCTComponentViewHelpers.h>
|
|
7
|
+
|
|
8
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
9
|
+
#import "SpallaPlayer-Swift.h"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
using namespace facebook::react;
|
|
13
|
+
|
|
14
|
+
@interface SpallaPlayerView () <RCTSpallaPlayerViewViewProtocol>
|
|
15
|
+
|
|
16
|
+
@end
|
|
17
|
+
|
|
18
|
+
@implementation SpallaPlayerView {
|
|
19
|
+
SpallaPlayerWrapper * _view;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider
|
|
23
|
+
{
|
|
24
|
+
return concreteComponentDescriptorProvider<SpallaPlayerViewComponentDescriptor>();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
28
|
+
{
|
|
29
|
+
if (self = [super initWithFrame:frame]) {
|
|
30
|
+
static const auto defaultProps = std::make_shared<const SpallaPlayerViewProps>();
|
|
31
|
+
_props = defaultProps;
|
|
32
|
+
|
|
33
|
+
[self ensureFreshWrapper];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return self;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
40
|
+
{
|
|
41
|
+
const auto &oldViewProps = *std::static_pointer_cast<SpallaPlayerViewProps const>(_props);
|
|
42
|
+
const auto &newViewProps = *std::static_pointer_cast<SpallaPlayerViewProps const>(props);
|
|
43
|
+
|
|
44
|
+
BOOL didRefresh = [self ensureFreshWrapper];
|
|
45
|
+
|
|
46
|
+
if (oldViewProps.contentId != newViewProps.contentId || didRefresh) {
|
|
47
|
+
[_view setContentId: [[NSString alloc] initWithUTF8String: newViewProps.contentId.c_str()]];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (oldViewProps.startTime != newViewProps.startTime || didRefresh) {
|
|
51
|
+
[_view setStartTime: [NSNumber numberWithDouble: newViewProps.startTime]];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (oldViewProps.hideUI != newViewProps.hideUI || didRefresh) {
|
|
55
|
+
[_view setHideUI: newViewProps.hideUI];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (oldViewProps.muted != newViewProps.muted || didRefresh) {
|
|
59
|
+
[_view setMuted: newViewProps.muted];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (oldViewProps.playbackRate != newViewProps.playbackRate || didRefresh) {
|
|
63
|
+
[_view setPlaybackRate: [NSNumber numberWithDouble: newViewProps.playbackRate]];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (oldViewProps.subtitle != newViewProps.subtitle || didRefresh) {
|
|
67
|
+
// SpallaPlayerViewProps::subtitle is a std::string (not an optional), so use empty() to check for "no subtitle".
|
|
68
|
+
if (!newViewProps.subtitle.empty()) {
|
|
69
|
+
[_view setSubtitle: [[NSString alloc] initWithUTF8String: newViewProps.subtitle.c_str()]];
|
|
70
|
+
} else {
|
|
71
|
+
[_view setSubtitle: nil];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
[super updateProps:props oldProps:oldProps];
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
- (BOOL)ensureFreshWrapper {
|
|
80
|
+
if (_view == nil) {
|
|
81
|
+
_view = [[SpallaPlayerWrapper alloc] init];
|
|
82
|
+
|
|
83
|
+
__weak __typeof(self) weakSelf = self;
|
|
84
|
+
[_view setOnPlayerEvent:^(NSDictionary<NSString *,id> * _Nonnull value) {
|
|
85
|
+
SpallaPlayerViewEventEmitter::OnPlayerEvent event = SpallaPlayerViewEventEmitter::OnPlayerEvent{
|
|
86
|
+
.event = std::string([[value objectForKey:@"event"] UTF8String]),
|
|
87
|
+
.duration = value[@"duration"] != nil ? [value[@"duration"] doubleValue] : 0.0,
|
|
88
|
+
.time = value[@"time"] != nil ? [value[@"time"] doubleValue] : 0.0,
|
|
89
|
+
.rate = value[@"rate"] != nil ? [value[@"rate"] doubleValue] : 0.0,
|
|
90
|
+
.subtitle = value[@"subtitle"] != nil ? std::string([value[@"subtitle"] UTF8String]) : std::string(),
|
|
91
|
+
.isLive = value[@"isLive"] != nil ? [value[@"isLive"] boolValue] : false,
|
|
92
|
+
.error = value[@"error"] != nil ? std::string([value[@"error"] UTF8String]) : std::string()
|
|
93
|
+
|
|
94
|
+
};
|
|
95
|
+
weakSelf.eventEmitter.onPlayerEvent(event);
|
|
96
|
+
}];
|
|
97
|
+
|
|
98
|
+
self.contentView = _view;
|
|
99
|
+
return YES;
|
|
100
|
+
}
|
|
101
|
+
return NO;
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (void)updateState:(const facebook::react::State::Shared &)state
|
|
106
|
+
oldState:(const facebook::react::State::Shared &)oldState {
|
|
107
|
+
[super updateState:state oldState:oldState];
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
112
|
+
{
|
|
113
|
+
RCTSpallaPlayerViewHandleCommand(self, commandName, args);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
- (void)pause {
|
|
117
|
+
[_view pause];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
- (void)play {
|
|
121
|
+
[_view play];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
- (void) seekTo:(double)time {
|
|
125
|
+
[_view seekToTime: time];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
- (void) selectPlaybackRate:(float)rate {
|
|
129
|
+
[_view selectPlaybackRate: rate];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
- (void) selectSubtitle:(NSString *)subtitle {
|
|
133
|
+
[_view selectSubtitle: subtitle];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
- (void) unmount {
|
|
137
|
+
[_view unmount];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- (void)prepareForRecycle {
|
|
141
|
+
[super prepareForRecycle];
|
|
142
|
+
|
|
143
|
+
[_view setOnPlayerEvent: nil];
|
|
144
|
+
[_view unmount];
|
|
145
|
+
_view = nil;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
- (const SpallaPlayerViewEventEmitter &)eventEmitter
|
|
149
|
+
{
|
|
150
|
+
return static_cast<const SpallaPlayerViewEventEmitter &>(*_eventEmitter);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Class<RCTComponentViewProtocol> SpallaPlayerViewCls(void)
|
|
154
|
+
{
|
|
155
|
+
return SpallaPlayerView.class;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@end
|