react-native-spalla-player 1.0.0 → 1.0.2

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.
@@ -1,204 +1,107 @@
1
1
  package com.spallaplayer
2
2
 
3
- import com.facebook.react.bridge.LifecycleEventListener
3
+ import android.util.Log
4
4
  import com.facebook.react.bridge.ReactApplicationContext
5
5
  import com.facebook.react.bridge.ReactContextBaseJavaModule
6
6
  import com.facebook.react.bridge.ReactMethod
7
7
  import com.facebook.react.bridge.UIManager
8
- import com.facebook.react.uimanager.NativeViewHierarchyManager
9
8
  import com.facebook.react.uimanager.UIManagerHelper
10
- import com.facebook.react.uimanager.UIManagerModule
11
9
  import com.facebook.react.uimanager.common.UIManagerType
12
10
  import com.spalla.sdk.android.core.SpallaSDK
13
11
  import com.spalla.sdk.android.core.player.view.SpallaPlayerView
14
12
 
15
-
16
- class SpallaPlayerModule(reactContext: ReactApplicationContext) :
17
- ReactContextBaseJavaModule(reactContext),
18
- LifecycleEventListener {
19
- private val _reactContext: ReactApplicationContext
13
+ class SpallaPlayerModule(
14
+ val reactContext: ReactApplicationContext
15
+ ): ReactContextBaseJavaModule(reactContext) {
20
16
 
21
17
  companion object {
22
- const val NAME = "RNSpallaPlayer"
23
- }
24
-
25
-
26
- init {
27
- reactContext.addLifecycleEventListener(this)
28
- _reactContext = reactContext
29
- }
30
-
31
- private fun isFabric(): Boolean {
32
- val uiManager = UIManagerHelper.getUIManager(this._reactContext, UIManagerType.FABRIC)
33
- // In Fabric, the UIManager is an instance of "FabricUIManager"
34
- return uiManager?.javaClass?.simpleName == "FabricUIManager"
35
- }
36
-
37
- override fun getName(): String {
38
- return "RNSpallaPlayer"
39
- }
40
-
41
- override fun onHostResume() {
18
+ val REACT_NAME: String = "SpallaPlayerModule"
42
19
  }
43
20
 
44
- override fun onHostPause() {
21
+ override fun getName() = REACT_NAME
45
22
 
23
+ @ReactMethod
24
+ fun play(tag: Double) {
25
+ reactContext.runOnUiQueueThread {
26
+ val view = getSpallaPlayerView(tag)
27
+ view?.play()
28
+ }
46
29
  }
47
30
 
48
- override fun onHostDestroy() {
31
+ @ReactMethod
32
+ fun pause(tag: Double) {
33
+ reactContext.runOnUiQueueThread {
34
+ val view = getSpallaPlayerView(tag)
35
+ view?.pause()
36
+ }
49
37
  }
50
38
 
51
39
  @ReactMethod
52
- fun play(tag: Int) {
53
- if (isFabric()) {
54
- val uiManager = UIManagerHelper.getUIManager(_reactContext, UIManagerType.FABRIC)
55
- if (uiManager is UIManager) {
56
- uiManager.resolveView(tag)?.let { view ->
57
- if (view is SpallaPlayerView) {
58
- view.play()
59
- } else {
60
- throw ClassCastException(
61
- "Cannot play: view with tag #$tag is not a SpallaPlayerView"
62
- )
63
- }
64
- }
65
- }
66
- } else {
67
- val uiManager: UIManagerModule? =
68
- _reactContext.getNativeModule(UIManagerModule::class.java)
69
- uiManager?.prependUIBlock { nativeViewHierarchyManager: NativeViewHierarchyManager ->
70
- val playerView = nativeViewHierarchyManager.resolveView(tag)
71
- if (playerView is SpallaPlayerView) {
72
- playerView.play()
73
- } else {
74
- throw ClassCastException(
75
- String.format(
76
- "Cannot play: view with tag #%d is not a SpallaPlayerView",
77
- tag
78
- )
79
- )
80
- }
81
- }
40
+ fun seekTo(tag: Double, time: Double) {
41
+ reactContext.runOnUiQueueThread {
42
+ val view = getSpallaPlayerView(tag)
43
+ view?.seekTo(time)
82
44
  }
83
45
  }
84
46
 
85
47
  @ReactMethod
86
- fun pause(tag: Int) {
87
- if (isFabric()) {
88
- val uiManager = UIManagerHelper.getUIManager(_reactContext, UIManagerType.FABRIC)
89
- if (uiManager is UIManager) {
90
- uiManager.resolveView(tag)?.let { view ->
91
- if (view is SpallaPlayerView) {
92
- view.pause()
93
- } else {
94
- throw ClassCastException(
95
- "Cannot pause: view with tag #$tag is not a SpallaPlayerView"
96
- )
97
- }
98
- }
99
- }
100
- } else {
101
- val uiManager: UIManagerModule? =
102
- _reactContext.getNativeModule(UIManagerModule::class.java)
103
- uiManager?.prependUIBlock { nativeViewHierarchyManager: NativeViewHierarchyManager ->
104
- val playerView = nativeViewHierarchyManager.resolveView(tag)
105
- if (playerView is SpallaPlayerView) {
106
- playerView.pause()
107
- } else {
108
- throw ClassCastException(
109
- String.format(
110
- "Cannot play: view with tag #%d is not a SpallaPplayerView",
111
- tag
112
- )
113
- )
114
- }
115
- }
48
+ fun selectSubtitle(tag: Double, subtitle: String?) {
49
+ reactContext.runOnUiQueueThread {
50
+ val view = getSpallaPlayerView(tag)
51
+ view?.selectSubtitle(subtitle)
116
52
  }
117
-
118
-
119
53
  }
120
54
 
121
55
  @ReactMethod
122
- fun seekTo(tag: Int, time: Double) {
123
- _reactContext.getNativeModule(UIManagerModule::class.java)!!
124
- .prependUIBlock { nativeViewHierarchyManager: NativeViewHierarchyManager ->
125
- val playerView = nativeViewHierarchyManager.resolveView(tag)
126
- if (playerView is SpallaPlayerView) {
127
- playerView.seekTo(time)
128
- } else {
129
- throw ClassCastException(
130
- String.format(
131
- "Cannot play: view with tag #%d is not a SpallaPplayerView",
132
- tag
133
- )
134
- )
135
- }
136
- }
56
+ fun selectPlaybackRate(tag: Double, rate: Double) {
57
+ reactContext.runOnUiQueueThread {
58
+ val view = getSpallaPlayerView(tag)
59
+ view?.selectPlaybackRate(rate)
60
+ }
137
61
  }
138
62
 
139
63
  @ReactMethod
140
- fun selectSubtitle(tag: Int, subtitle: String?) {
141
- _reactContext.getNativeModule(UIManagerModule::class.java)!!
142
- .prependUIBlock { nativeViewHierarchyManager: NativeViewHierarchyManager ->
143
- val playerView = nativeViewHierarchyManager.resolveView(tag)
144
- if (playerView is SpallaPlayerView) {
145
- playerView.selectSubtitle(subtitle)
146
- } else {
147
- throw ClassCastException(
148
- String.format(
149
- "Cannot play: view with tag #%d is not a SpallaPplayerView",
150
- tag
151
- )
152
- )
153
- }
154
- }
64
+ fun unmount(tag: Double) {
65
+ try {
66
+ pause(tag)
67
+ } catch (e: Exception) {
68
+ Log.e("SpallaPlayerModule", "Error in unmount method", e)
69
+ }
155
70
  }
156
71
 
157
72
  @ReactMethod
158
- fun selectPlaybackRate(tag: Int, rate: Double) {
159
- _reactContext.getNativeModule(UIManagerModule::class.java)!!
160
- .prependUIBlock { nativeViewHierarchyManager: NativeViewHierarchyManager ->
161
- val playerView = nativeViewHierarchyManager.resolveView(tag)
162
- if (playerView is SpallaPlayerView) {
163
- playerView.selectPlaybackRate(rate)
164
- } else {
165
- throw ClassCastException(
166
- String.format(
167
- "Cannot play: view with tag #%d is not a SpallaPplayerView",
168
- tag
169
- )
170
- )
171
- }
73
+ fun initialize(token: String?, applicationId: String?) {
74
+ try {
75
+ if (token.isNullOrBlank()) {
76
+ throw IllegalArgumentException("Token cannot be null or empty")
172
77
  }
78
+ SpallaSDK.initialize(reactContext, token)
79
+ } catch (e: Exception) {
80
+ Log.e("SpallaPlayerModule", "Failed to initialize SpallaSDK", e)
81
+ }
173
82
  }
174
83
 
175
- @ReactMethod
176
- fun unmount(tag: Int) {
177
- if (isFabric()) {
178
- val uiManager = UIManagerHelper.getUIManager(_reactContext, UIManagerType.FABRIC)
84
+ fun getSpallaPlayerView(tag: Double): SpallaPlayerView? {
85
+ try {
86
+ val uiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)
179
87
  if (uiManager is UIManager) {
180
- uiManager.resolveView(tag)?.let { view ->
181
- if (view is SpallaPlayerView) {
182
- view.pause()
183
- }
184
- }
185
- }
186
- } else {
187
- val uiManager: UIManagerModule? =
188
- _reactContext.getNativeModule(UIManagerModule::class.java)
189
- uiManager?.prependUIBlock { nativeViewHierarchyManager: NativeViewHierarchyManager ->
190
- val playerView = nativeViewHierarchyManager.resolveView(tag)
191
- if (playerView is SpallaPlayerView) {
192
- playerView.pause()
88
+ val view = uiManager.resolveView(tag.toInt())
89
+ if (view is SpallaPlayerView) {
90
+ return view
91
+ } else if (view != null) {
92
+ Log.e(
93
+ "SpallaPlayerModule",
94
+ "Cannot play: view with tag #$tag is not a SpallaPlayerView"
95
+ )
96
+ } else {
97
+ Log.e("SpallaPlayerModule", "Cannot find view with tag #$tag")
193
98
  }
99
+ } else {
100
+ Log.e("SpallaPlayerModule", "UIManager is not available")
194
101
  }
102
+ } catch (e: Exception) {
103
+ Log.e("SpallaPlayerModule", "Error in play method", e)
195
104
  }
105
+ return null
196
106
  }
197
-
198
- @ReactMethod
199
- fun initialize(token: String, applicationId: String?) {
200
- SpallaSDK.initialize(_reactContext, token)
201
- }
202
-
203
-
204
107
  }
@@ -1,30 +1,44 @@
1
1
  package com.spallaplayer
2
2
 
3
- import com.facebook.react.ReactPackage
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.ModuleSpec
4
5
  import com.facebook.react.bridge.NativeModule
5
6
  import com.facebook.react.bridge.ReactApplicationContext
6
7
  import com.facebook.react.module.model.ReactModuleInfo
7
8
  import com.facebook.react.module.model.ReactModuleInfoProvider
8
- import com.facebook.react.uimanager.ViewManager
9
9
  import com.spallaplayer.components.RNGoogleCastButtonManager
10
- import java.util.ArrayList
11
10
 
12
- class SpallaPlayerViewPackage : ReactPackage {
13
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
14
- val viewManagers: MutableList<ViewManager<*, *>> = ArrayList()
15
- viewManagers.add(SpallaPlayerViewManager())
16
- viewManagers.add(RNGoogleCastButtonManager())
17
- return viewManagers
18
- }
11
+ class SpallaPlayerViewPackage : BaseReactPackage() {
19
12
 
20
- @Deprecated("Migrate to [BaseReactPackage] and implement [getModule] instead.")
21
- override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
22
- return listOf(SpallaPlayerModuleFabric(reactContext))
23
- }
13
+ override fun getViewManagers(
14
+ reactContext: ReactApplicationContext
15
+ ): List<ModuleSpec> =
16
+ listOf(
17
+ ModuleSpec.viewManagerSpec {
18
+ SpallaPlayerViewManager()
19
+ },
20
+ ModuleSpec.viewManagerSpec {
21
+ RNGoogleCastButtonManager()
22
+ }
23
+ )
24
+
25
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider =
26
+ ReactModuleInfoProvider {
27
+ mapOf(
28
+ SpallaPlayerModule.REACT_NAME to ReactModuleInfo(
29
+ SpallaPlayerModule.REACT_NAME,
30
+ className = SpallaPlayerModule::class.java.name,
31
+ canOverrideExistingModule = false,
32
+ needsEagerInit = false,
33
+ isCxxModule = false,
34
+ isTurboModule = false
35
+ )
36
+ )
37
+ }
24
38
 
25
39
  override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
26
40
  return when (name) {
27
- SpallaPlayerModule.NAME -> SpallaPlayerModuleFabric(reactContext)
41
+ SpallaPlayerModule.REACT_NAME -> SpallaPlayerModule(reactContext)
28
42
  else -> null
29
43
  }
30
44
  }
@@ -1,61 +1,92 @@
1
+ package com.spallaplayer
2
+
1
3
  import android.util.Log
2
4
  import android.view.View
3
5
  import com.facebook.react.bridge.Arguments
4
6
  import com.facebook.react.bridge.ReactContext
7
+ import com.facebook.react.bridge.ReadableArray
5
8
  import com.facebook.react.bridge.WritableMap
9
+ import com.facebook.react.module.annotations.ReactModule
6
10
  import com.facebook.react.uimanager.ViewGroupManager
7
11
  import com.facebook.react.uimanager.ThemedReactContext
12
+ import com.facebook.react.uimanager.UIManagerHelper
13
+ import com.facebook.react.uimanager.ViewManagerDelegate
8
14
  import com.facebook.react.uimanager.annotations.ReactProp
9
- import com.facebook.react.uimanager.events.RCTEventEmitter
10
15
  import com.spalla.sdk.android.core.player.entities.SpallaPlayerEvent
11
16
  import com.spalla.sdk.android.core.player.entities.SpallaPlayerEvent.*
12
17
  import com.spalla.sdk.android.core.player.listeners.SpallaPlayerFullScreenListener
13
18
  import com.spalla.sdk.android.core.player.listeners.SpallaPlayerListener
14
19
  import com.spalla.sdk.android.core.player.view.SpallaPlayerView
15
-
16
-
17
20
  import java.util.Timer
18
- import java.util.TimerTask
19
21
 
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)
20
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: SpallaPlayerContainerView? = null
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() = "RNSpallaPlayer"
54
+ override fun getName() = REACT_CLASS
34
55
 
35
56
  override fun createViewInstance(context: ThemedReactContext): SpallaPlayerView {
36
57
  _reactContext = context
58
+ //val container = SpallaPlayerContainerView(context)
37
59
  val player = SpallaPlayerView(context)
38
- _playerView = player
39
- _playerView?.registerPlayerListener(this)
40
- _playerView?.registerFullScreenListener(this)
41
- return player
42
- }
43
60
 
44
- override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> {
45
- val eventMap = mutableMapOf<String, Any>()
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
+ }
46
76
 
47
- eventMap["onPlayerEvent"] = mapOf(
48
- "phasedRegistrationNames" to mapOf(
49
- "bubbled" to "onPlayerEvent"
50
- )
51
- )
77
+ override fun onViewDetachedFromWindow(view: View) {
78
+ _playerView?.pause()
79
+
80
+ }
81
+ })
52
82
 
53
- return eventMap
83
+ //this._container = container
84
+ //return container
85
+ return player
54
86
  }
55
87
 
56
88
  override fun onDropViewInstance(view: SpallaPlayerView) {
57
89
  Log.v("RNSpallaPlayerManager", "onDropViewInstance")
58
-
59
90
  try {
60
91
  view.onDestroy()
61
92
  } catch (e: Exception) {
@@ -64,83 +95,119 @@ class SpallaPlayerViewManager() : ViewGroupManager<SpallaPlayerView>(),
64
95
  super.onDropViewInstance(view)
65
96
  }
66
97
 
67
- override fun addView(parent: SpallaPlayerView, child: View, index: Int) {
68
- parent.addView(child, index)
69
- }
70
-
71
- override fun removeViewAt(parent: SpallaPlayerView, index: Int) {
72
- parent.removeViewAt(index)
73
- }
74
-
75
- override fun getChildCount(parent: SpallaPlayerView): Int {
76
- return parent.childCount
77
- }
78
-
79
- override fun getChildAt(parent: SpallaPlayerView, index: Int): View {
80
- return parent.getChildAt(index)
81
- }
82
-
98
+ // --- Prop Setters ---
83
99
  @ReactProp(name = "contentId")
84
- fun setContentId(view: SpallaPlayerView, contentId: String) {
85
- this.contentId = contentId
86
- checkAndLoadPlayer(view)
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
+ }
87
110
  }
88
111
 
89
112
  @ReactProp(name = "muted")
90
- fun setMuted(view: SpallaPlayerView, muted: Boolean) {
91
- _playerView?.setMuted(muted)
113
+ override fun setMuted(view: SpallaPlayerView, value: Boolean) {
114
+ _playerView?.setMuted(value)
92
115
  }
93
116
 
94
117
  @ReactProp(name = "startTime")
95
- fun setStartTime(view: SpallaPlayerView, startTime: Double) {
96
- this.startTime = startTime
118
+ override fun setStartTime(view: SpallaPlayerView, value: Double) {
119
+ this.startTime = value
97
120
  //checkAndLoadPlayer(view)
98
121
  }
99
122
 
100
123
  @ReactProp(name = "subtitle")
101
- fun setSubtitle(view: SpallaPlayerView, subtitle: String?) {
102
- this.subtitle = subtitle
124
+ override fun setSubtitle(view: SpallaPlayerView, value: String?) {
125
+ this.subtitle = value
103
126
  _playerView?.selectSubtitle(subtitle)
104
127
  }
105
128
 
106
129
  @ReactProp(name = "playbackRate")
107
- fun setPlaybackRate(view: SpallaPlayerView, playbackRate: Double) {
108
- if (playbackRate > 0) {
109
- this.playbackRate = playbackRate
130
+ override fun setPlaybackRate(view: SpallaPlayerView, value: Float) {
131
+ if (value > 0) {
132
+ this.playbackRate = value.toDouble()
110
133
  _playerView?.selectPlaybackRate(playbackRate)
111
134
  }
112
135
  }
113
136
 
114
137
  @ReactProp(name = "hideUI")
115
- fun setHideUI(view: SpallaPlayerView, hideUI: Boolean) {
116
- this.hideUI = hideUI
117
- checkAndLoadPlayer(view)
138
+ override fun setHideUI(view: SpallaPlayerView, value: Boolean) {
139
+ this.hideUI = value
140
+ //checkAndLoadPlayer(view)
141
+ }
142
+
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)
163
+ }
164
+
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()
118
175
  }
119
176
 
120
177
  private fun checkAndLoadPlayer(view: SpallaPlayerView) {
121
- if (contentId != null && startTime != null && hideUI != null) {
122
- view.load(contentId!!, true, startTime!!, subtitle, hideUI!!)
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!!)
182
+ }
183
+ } catch (e: Exception) {
184
+ Log.e("SpallaPlayerViewManager", "Error loading player", e)
123
185
  }
124
186
  }
125
187
 
126
- private fun startLoadTimer(view: SpallaPlayerView) {
127
- // Cancel any existing timer
128
- loadTimer?.cancel()
188
+ // --- Command Handling ---
189
+ override fun receiveCommand(
190
+ root: SpallaPlayerView,
191
+ commandId: String,
192
+ args: ReadableArray?
193
+ ) {
194
+ mDelegate.receiveCommand(root, commandId, args)
195
+ }
129
196
 
130
- // Start a new timer to check if all required properties are set
131
- loadTimer = Timer()
132
- loadTimer?.schedule(object : TimerTask() {
133
- override fun run() {
134
- loadTimer?.cancel()
135
- loadTimer = null
136
- // Call view.load with the required properties
137
- view.load(contentId!!, true, startTime!!, subtitle = subtitle, hideUI!!)
138
- }
139
- }, 300) // Delay of 200ms to allow all properties to be set
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
+ )
140
206
  }
141
207
 
142
208
  override fun onEvent(event: SpallaPlayerEvent) {
143
209
  val map: WritableMap = Arguments.createMap()
210
+ val eventName = "onPlayerEvent"
144
211
 
145
212
  Log.v("RNSpallaPlayerManager", "onEvent: $event")
146
213
  when (event) {
@@ -148,13 +215,11 @@ class SpallaPlayerViewManager() : ViewGroupManager<SpallaPlayerView>(),
148
215
  map.putString("event", "durationUpdate")
149
216
  map.putDouble("duration", event.duration)
150
217
  }
151
-
152
218
  Ended -> map.putString("event", "ended")
153
219
  is Error -> {
154
220
  map.putString("event", "error")
155
221
  map.putString("message", event.message)
156
222
  }
157
-
158
223
  Pause -> map.putString("event", "pause")
159
224
  Play -> map.putString("event", "play")
160
225
  Playing -> map.putString("event", "playing")
@@ -162,69 +227,63 @@ class SpallaPlayerViewManager() : ViewGroupManager<SpallaPlayerView>(),
162
227
  map.putString("event", "timeUpdate")
163
228
  map.putDouble("time", event.currentTime)
164
229
  }
165
-
166
230
  Waiting -> map.putString("event", "buffering")
167
231
  is SubtitlesAvailable -> {
168
232
  map.putString("event", "subtitlesAvailable")
169
-
170
- var subs = Arguments.createArray()
171
- event.subtitles.forEach {
172
- subs.pushString(it)
173
- }
233
+ val subs = Arguments.createArray()
234
+ event.subtitles.forEach { subs.pushString(it) }
174
235
  map.putArray("subtitles", subs)
175
236
  }
176
-
177
237
  is SubtitleSelected -> {
178
238
  map.putString("event", "subtitleSelected")
179
239
  map.putString("subtitle", event.subtitle)
180
240
  }
181
-
182
241
  is MetadataLoaded -> {
183
242
  map.putString("event", "metadataLoaded")
184
243
  map.putBoolean("isLive", event.metadata.isLive)
185
244
  map.putDouble("duration", event.metadata.duration)
186
-
187
- // make sure current playback rate is applied if set initially
188
245
  _playerView?.selectPlaybackRate(playbackRate)
189
246
  }
190
-
191
247
  is PlaybackRateSelected -> {
192
248
  map.putString("event", "playbackRateSelected")
193
249
  map.putDouble("rate", event.rate)
194
250
  }
195
-
196
251
  }
197
- _playerView?.let { container ->
198
- _reactContext?.getJSModule(RCTEventEmitter::class.java)?.receiveEvent(
199
- container.id,
200
- "onPlayerEvent",
201
- map
202
- )
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
+ }
203
261
  }
204
262
  }
205
263
 
206
264
  override fun onEnterFullScreen() {
207
- Log.v("SpallaPlayerViewManager", "onEnterFullScreen")
208
265
  val map: WritableMap = Arguments.createMap()
209
266
  map.putString("event", "enterFullScreen")
210
- _playerView?.let { container ->
211
- _reactContext?.getJSModule(RCTEventEmitter::class.java)?.receiveEvent(
212
- container.id,
213
- "onPlayerEvent",
214
- map
215
- )
216
- }
267
+ dispatchEvent("onPlayerEvent", map)
217
268
  }
218
269
 
219
270
  override fun onExitFullScreen() {
220
271
  val map: WritableMap = Arguments.createMap()
221
272
  map.putString("event", "exitFullScreen")
222
- _playerView?.let { container ->
223
- _reactContext?.getJSModule(RCTEventEmitter::class.java)?.receiveEvent(
224
- container.id,
225
- "onPlayerEvent",
226
- map
227
- )
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
+ }
228
287
  }
229
288
  }
230
289
 
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "react-native-spalla-player",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Spalla Player for React Native",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
9
  "source": "./src/index.tsx",
10
+ "import": "./lib/module/index.js",
10
11
  "types": "./lib/typescript/src/index.d.ts",
11
12
  "default": "./lib/module/index.js"
12
13
  },
@@ -1,103 +0,0 @@
1
- package com.spallaplayer
2
-
3
- import android.util.Log
4
- import com.facebook.react.bridge.ReactApplicationContext
5
- import com.facebook.react.bridge.ReactContextBaseJavaModule
6
- import com.facebook.react.bridge.ReactMethod
7
- import com.facebook.react.bridge.UIManager
8
- import com.facebook.react.uimanager.UIManagerHelper
9
- import com.facebook.react.uimanager.common.UIManagerType
10
- import com.spalla.sdk.android.core.SpallaSDK
11
- import com.spalla.sdk.android.core.player.view.SpallaPlayerView
12
-
13
- class SpallaPlayerModuleFabric(
14
- val reactContext: ReactApplicationContext
15
- ): ReactContextBaseJavaModule(reactContext) {
16
-
17
- override fun getName() = "SpallaPlayerModule"
18
-
19
- @ReactMethod
20
- fun play(tag: Double) {
21
- reactContext.runOnUiQueueThread {
22
- val view = getSpallaPlayerView(tag)
23
- view?.play()
24
- }
25
- }
26
-
27
- @ReactMethod
28
- fun pause(tag: Double) {
29
- reactContext.runOnUiQueueThread {
30
- val view = getSpallaPlayerView(tag)
31
- view?.pause()
32
- }
33
- }
34
-
35
- @ReactMethod
36
- fun seekTo(tag: Double, time: Double) {
37
- reactContext.runOnUiQueueThread {
38
- val view = getSpallaPlayerView(tag)
39
- view?.seekTo(time)
40
- }
41
- }
42
-
43
- @ReactMethod
44
- fun selectSubtitle(tag: Double, subtitle: String?) {
45
- reactContext.runOnUiQueueThread {
46
- val view = getSpallaPlayerView(tag)
47
- view?.selectSubtitle(subtitle)
48
- }
49
- }
50
-
51
- @ReactMethod
52
- fun selectPlaybackRate(tag: Double, rate: Double) {
53
- reactContext.runOnUiQueueThread {
54
- val view = getSpallaPlayerView(tag)
55
- view?.selectPlaybackRate(rate)
56
- }
57
- }
58
-
59
- @ReactMethod
60
- fun unmount(tag: Double) {
61
- try {
62
- pause(tag)
63
- } catch (e: Exception) {
64
- Log.e("SpallaPlayerModule", "Error in unmount method", e)
65
- }
66
- }
67
-
68
- @ReactMethod
69
- fun initialize(token: String?, applicationId: String?) {
70
- try {
71
- if (token.isNullOrBlank()) {
72
- throw IllegalArgumentException("Token cannot be null or empty")
73
- }
74
- SpallaSDK.initialize(reactContext, token)
75
- } catch (e: Exception) {
76
- Log.e("SpallaPlayerModule", "Failed to initialize SpallaSDK", e)
77
- }
78
- }
79
-
80
- fun getSpallaPlayerView(tag: Double): SpallaPlayerView? {
81
- try {
82
- val uiManager = UIManagerHelper.getUIManager(reactContext, UIManagerType.FABRIC)
83
- if (uiManager is UIManager) {
84
- val view = uiManager.resolveView(tag.toInt())
85
- if (view is SpallaPlayerView) {
86
- return view
87
- } else if (view != null) {
88
- Log.e(
89
- "SpallaPlayerModule",
90
- "Cannot play: view with tag #$tag is not a SpallaPlayerView"
91
- )
92
- } else {
93
- Log.e("SpallaPlayerModule", "Cannot find view with tag #$tag")
94
- }
95
- } else {
96
- Log.e("SpallaPlayerModule", "UIManager is not available")
97
- }
98
- } catch (e: Exception) {
99
- Log.e("SpallaPlayerModule", "Error in play method", e)
100
- }
101
- return null
102
- }
103
- }
@@ -1,15 +0,0 @@
1
- package com.spallaplayer
2
-
3
- import android.content.Context
4
- import android.util.AttributeSet
5
- import android.view.View
6
-
7
- class SpallaPlayerView : View {
8
- constructor(context: Context?) : super(context)
9
- constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
10
- constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
11
- context,
12
- attrs,
13
- defStyleAttr
14
- )
15
- }
@@ -1,289 +0,0 @@
1
- package com.spallaplayer
2
-
3
- import android.util.Log
4
- import android.view.View
5
- import com.facebook.react.bridge.Arguments
6
- import com.facebook.react.bridge.ReactContext
7
- import com.facebook.react.bridge.ReadableArray
8
- import com.facebook.react.bridge.WritableMap
9
- import com.facebook.react.module.annotations.ReactModule
10
- import com.facebook.react.uimanager.ViewGroupManager
11
- import com.facebook.react.uimanager.ThemedReactContext
12
- import com.facebook.react.uimanager.UIManagerHelper
13
- import com.facebook.react.uimanager.annotations.ReactProp
14
- import com.spalla.sdk.android.core.player.entities.SpallaPlayerEvent
15
- import com.spalla.sdk.android.core.player.entities.SpallaPlayerEvent.*
16
- import com.spalla.sdk.android.core.player.listeners.SpallaPlayerFullScreenListener
17
- import com.spalla.sdk.android.core.player.listeners.SpallaPlayerListener
18
- import com.spalla.sdk.android.core.player.view.SpallaPlayerView
19
- import java.util.Timer
20
-
21
- // These imports will work because Codegen runs for Fabric builds.
22
- // Your IDE will show errors on these lines until you build the project.
23
- import com.facebook.react.viewmanagers.SpallaPlayerViewManagerInterface
24
- import com.facebook.react.viewmanagers.SpallaPlayerViewManagerDelegate
25
-
26
- // Implement the Codegen interface for Fabric support
27
- @ReactModule(name = SpallaPlayerViewManager.REACT_CLASS)
28
- class SpallaPlayerViewManager() : ViewGroupManager<SpallaPlayerView>(),
29
- SpallaPlayerViewManagerInterface<SpallaPlayerView>, // Fabric interface
30
- SpallaPlayerListener, SpallaPlayerFullScreenListener {
31
-
32
- companion object {
33
- const val REACT_CLASS = "SpallaPlayerView"
34
- }
35
-
36
- // The delegate is required for Fabric to handle props and commands.
37
- private val mDelegate = SpallaPlayerViewManagerDelegate(this)
38
-
39
- // This method is required by the Fabric architecture.
40
- override fun getDelegate() = mDelegate
41
-
42
- private var _playerView: SpallaPlayerView? = null
43
- private var _reactContext: ReactContext? = null
44
- //private var _container: SpallaPlayerView? = null
45
- private var contentId: String? = null
46
- private var startTime: Double? = null
47
- private var subtitle: String? = null
48
- private var loadTimer: Timer? = null
49
- private var playbackRate: Double = 1.0
50
- private var hideUI: Boolean? = null
51
- private var _viewTag: Int = -1
52
-
53
- override fun getName() = REACT_CLASS
54
-
55
- override fun createViewInstance(context: ThemedReactContext): SpallaPlayerView {
56
- _reactContext = context
57
- //val container = SpallaPlayerContainerView(context)
58
- val player = SpallaPlayerView(context)
59
-
60
- // Defer initialization until view is attached to window
61
- player.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
62
- override fun onViewAttachedToWindow(view: View) {
63
- try {
64
- _playerView = player
65
- _playerView?.registerPlayerListener(this@SpallaPlayerViewManager)
66
- _playerView?.registerFullScreenListener(this@SpallaPlayerViewManager)
67
- // Remove listener to avoid memory leaks
68
- view.removeOnAttachStateChangeListener(this)
69
-
70
- checkAndLoadPlayer(player)
71
- } catch (e: Exception) {
72
- Log.e("SpallaPlayerViewManager", "Error during view attachment", e)
73
- }
74
- }
75
-
76
- override fun onViewDetachedFromWindow(view: View) {
77
- _playerView?.pause()
78
-
79
- }
80
- })
81
-
82
- //this._container = container
83
- //return container
84
- return player
85
- }
86
-
87
- override fun onDropViewInstance(view: SpallaPlayerView) {
88
- Log.v("RNSpallaPlayerManager", "onDropViewInstance")
89
- try {
90
- view.onDestroy()
91
- } catch (e: Exception) {
92
- e.printStackTrace()
93
- }
94
- super.onDropViewInstance(view)
95
- }
96
-
97
- // --- Prop Setters ---
98
- @ReactProp(name = "contentId")
99
- override fun setContentId(view: SpallaPlayerView, value: String?) {
100
- _viewTag = view.tag as? Int ?: -1
101
- if (value != null) {
102
- this.contentId = value
103
- //add a hacky delay here, so if content and starttime are changed at same time,
104
- //start time is set first
105
- view.postDelayed({
106
- checkAndLoadPlayer(view)
107
- }, 100L)
108
- }
109
- }
110
-
111
- @ReactProp(name = "muted")
112
- override fun setMuted(view: SpallaPlayerView, value: Boolean) {
113
- _playerView?.setMuted(value)
114
- }
115
-
116
- @ReactProp(name = "startTime")
117
- override fun setStartTime(view: SpallaPlayerView, value: Double) {
118
- this.startTime = value
119
- //checkAndLoadPlayer(view)
120
- }
121
-
122
- @ReactProp(name = "subtitle")
123
- override fun setSubtitle(view: SpallaPlayerView, value: String?) {
124
- this.subtitle = value
125
- _playerView?.selectSubtitle(subtitle)
126
- }
127
-
128
- @ReactProp(name = "playbackRate")
129
- override fun setPlaybackRate(view: SpallaPlayerView, value: Float) {
130
- if (value > 0) {
131
- this.playbackRate = value.toDouble()
132
- _playerView?.selectPlaybackRate(playbackRate)
133
- }
134
- }
135
-
136
- @ReactProp(name = "hideUI")
137
- override fun setHideUI(view: SpallaPlayerView, value: Boolean) {
138
- this.hideUI = value
139
- //checkAndLoadPlayer(view)
140
- }
141
-
142
- override fun play(view: SpallaPlayerView?) {
143
- view?.play()
144
- }
145
-
146
- override fun pause(view: SpallaPlayerView?) {
147
- view?.pause()
148
- }
149
-
150
- override fun seekTo(
151
- view: SpallaPlayerView?,
152
- time: Double
153
- ) {
154
- view?.seekTo(time)
155
- }
156
-
157
- override fun selectSubtitle(
158
- view: SpallaPlayerView?,
159
- subtitle: String?
160
- ) {
161
- view?.selectSubtitle(subtitle)
162
- }
163
-
164
- override fun selectPlaybackRate(
165
- view: SpallaPlayerView?,
166
- rate: Float
167
- ) {
168
- view?.selectPlaybackRate(rate.toDouble())
169
- }
170
-
171
- override fun unmount(view: SpallaPlayerView?) {
172
- view?.pause()
173
- view?.onDestroy()
174
- }
175
-
176
- private fun checkAndLoadPlayer(view: SpallaPlayerView) {
177
- try {
178
- // Only load if view is attached to window and all required props are set
179
- if (contentId != null && startTime != null && hideUI != null && view.isAttachedToWindow) {
180
- view.load(contentId!!, true, startTime!!, subtitle, hideUI!!)
181
- }
182
- } catch (e: Exception) {
183
- Log.e("SpallaPlayerViewManager", "Error loading player", e)
184
- }
185
- }
186
-
187
- // --- Command Handling ---
188
- override fun receiveCommand(
189
- root: SpallaPlayerView,
190
- commandId: String,
191
- args: ReadableArray?
192
- ) {
193
- mDelegate.receiveCommand(root, commandId, args)
194
- }
195
-
196
- // --- Event Handling ---
197
- override fun getExportedCustomBubblingEventTypeConstants(): MutableMap<String, Any> {
198
- return mutableMapOf(
199
- "onPlayerEvent" to mapOf(
200
- "phasedRegistrationNames" to mapOf(
201
- "bubbled" to "onPlayerEvent"
202
- )
203
- )
204
- )
205
- }
206
-
207
- override fun onEvent(event: SpallaPlayerEvent) {
208
- val map: WritableMap = Arguments.createMap()
209
- val eventName = "onPlayerEvent"
210
-
211
- Log.v("RNSpallaPlayerManager", "onEvent: $event")
212
- when (event) {
213
- is DurationUpdate -> {
214
- map.putString("event", "durationUpdate")
215
- map.putDouble("duration", event.duration)
216
- }
217
- Ended -> map.putString("event", "ended")
218
- is Error -> {
219
- map.putString("event", "error")
220
- map.putString("message", event.message)
221
- }
222
- Pause -> map.putString("event", "pause")
223
- Play -> map.putString("event", "play")
224
- Playing -> map.putString("event", "playing")
225
- is TimeUpdate -> {
226
- map.putString("event", "timeUpdate")
227
- map.putDouble("time", event.currentTime)
228
- }
229
- Waiting -> map.putString("event", "buffering")
230
- is SubtitlesAvailable -> {
231
- map.putString("event", "subtitlesAvailable")
232
- val subs = Arguments.createArray()
233
- event.subtitles.forEach { subs.pushString(it) }
234
- map.putArray("subtitles", subs)
235
- }
236
- is SubtitleSelected -> {
237
- map.putString("event", "subtitleSelected")
238
- map.putString("subtitle", event.subtitle)
239
- }
240
- is MetadataLoaded -> {
241
- map.putString("event", "metadataLoaded")
242
- map.putBoolean("isLive", event.metadata.isLive)
243
- map.putDouble("duration", event.metadata.duration)
244
- _playerView?.selectPlaybackRate(playbackRate)
245
- }
246
- is PlaybackRateSelected -> {
247
- map.putString("event", "playbackRateSelected")
248
- map.putDouble("rate", event.rate)
249
- }
250
- }
251
-
252
- _playerView?.let { view ->
253
- _reactContext?.let { reactContext ->
254
- val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
255
- val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id)
256
- eventDispatcher?.dispatchEvent(
257
- RNSpallaPlayerEvent(surfaceId, view.id, eventName, map)
258
- )
259
- }
260
- }
261
- }
262
-
263
- override fun onEnterFullScreen() {
264
- val map: WritableMap = Arguments.createMap()
265
- map.putString("event", "enterFullScreen")
266
- dispatchEvent("onPlayerEvent", map)
267
- }
268
-
269
- override fun onExitFullScreen() {
270
- val map: WritableMap = Arguments.createMap()
271
- map.putString("event", "exitFullScreen")
272
- dispatchEvent("onPlayerEvent", map)
273
- }
274
-
275
- private fun dispatchEvent(eventName: String, eventData: WritableMap) {
276
- _playerView?.let { view ->
277
- _reactContext?.let { reactContext ->
278
- if (_viewTag != -1) {
279
- val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
280
- val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, view.id)
281
- eventDispatcher?.dispatchEvent(
282
- RNSpallaPlayerEvent(surfaceId, _viewTag, eventName, eventData)
283
- )
284
- }
285
- }
286
- }
287
- }
288
-
289
- }