@granite-js/video 1.0.0
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/CHANGELOG.md +7 -0
- package/GraniteVideo.podspec +72 -0
- package/android/README.md +232 -0
- package/android/build.gradle +117 -0
- package/android/gradle.properties +8 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/run/granite/video/GraniteVideoModule.kt +70 -0
- package/android/src/main/java/run/granite/video/GraniteVideoPackage.kt +43 -0
- package/android/src/main/java/run/granite/video/GraniteVideoView.kt +384 -0
- package/android/src/main/java/run/granite/video/GraniteVideoViewManager.kt +318 -0
- package/android/src/main/java/run/granite/video/event/GraniteVideoEvents.kt +273 -0
- package/android/src/main/java/run/granite/video/event/VideoEventDispatcher.kt +66 -0
- package/android/src/main/java/run/granite/video/event/VideoEventListenerAdapter.kt +157 -0
- package/android/src/main/java/run/granite/video/provider/GraniteVideoProvider.kt +346 -0
- package/android/src/media3/AndroidManifest.xml +9 -0
- package/android/src/media3/java/run/granite/video/provider/media3/ExoPlayerProvider.kt +386 -0
- package/android/src/media3/java/run/granite/video/provider/media3/Media3ContentProvider.kt +29 -0
- package/android/src/media3/java/run/granite/video/provider/media3/Media3Initializer.kt +25 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/ExoPlayerFactory.kt +32 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/MediaSourceFactory.kt +61 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/TrackSelectorFactory.kt +26 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/VideoSurfaceFactory.kt +62 -0
- package/android/src/media3/java/run/granite/video/provider/media3/listener/ExoPlayerEventListener.kt +104 -0
- package/android/src/media3/java/run/granite/video/provider/media3/scheduler/ProgressScheduler.kt +56 -0
- package/android/src/test/java/run/granite/video/GraniteVideoViewRobolectricTest.kt +598 -0
- package/android/src/test/java/run/granite/video/event/VideoEventListenerAdapterTest.kt +319 -0
- package/android/src/test/java/run/granite/video/helpers/FakeGraniteVideoProvider.kt +161 -0
- package/android/src/test/java/run/granite/video/helpers/TestProgressScheduler.kt +42 -0
- package/android/src/test/java/run/granite/video/provider/GraniteVideoRegistryTest.kt +232 -0
- package/android/src/test/java/run/granite/video/provider/ProviderContractTest.kt +174 -0
- package/android/src/test/java/run/granite/video/provider/media3/listener/ExoPlayerEventListenerTest.kt +243 -0
- package/android/src/test/resources/kotest.properties +2 -0
- package/dist/module/GraniteVideo.js +458 -0
- package/dist/module/GraniteVideo.js.map +1 -0
- package/dist/module/GraniteVideoNativeComponent.ts +265 -0
- package/dist/module/index.js +7 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/types.js +4 -0
- package/dist/module/types.js.map +1 -0
- package/dist/typescript/GraniteVideo.d.ts +12 -0
- package/dist/typescript/GraniteVideoNativeComponent.d.ts +189 -0
- package/dist/typescript/index.d.ts +5 -0
- package/dist/typescript/types.d.ts +328 -0
- package/ios/GraniteVideoComponentsProvider.h +10 -0
- package/ios/GraniteVideoProvider.swift +280 -0
- package/ios/GraniteVideoView.h +15 -0
- package/ios/GraniteVideoView.mm +661 -0
- package/ios/Providers/AVPlayerProvider.swift +541 -0
- package/package.json +106 -0
- package/src/GraniteVideo.tsx +575 -0
- package/src/GraniteVideoNativeComponent.ts +265 -0
- package/src/index.ts +8 -0
- package/src/types.ts +464 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
package run.granite.video.event
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import com.facebook.react.uimanager.events.Event
|
|
6
|
+
import io.kotest.core.spec.style.FunSpec
|
|
7
|
+
import io.kotest.matchers.shouldBe
|
|
8
|
+
import io.mockk.*
|
|
9
|
+
import run.granite.video.provider.GraniteVideoErrorData
|
|
10
|
+
import run.granite.video.provider.GraniteVideoLoadData
|
|
11
|
+
import run.granite.video.provider.GraniteVideoProgressData
|
|
12
|
+
|
|
13
|
+
class VideoEventListenerAdapterTest : FunSpec({
|
|
14
|
+
|
|
15
|
+
lateinit var mockDispatcher: VideoEventDispatcher
|
|
16
|
+
lateinit var mockWritableMap: WritableMap
|
|
17
|
+
lateinit var adapter: VideoEventListenerAdapter
|
|
18
|
+
val viewId = 123
|
|
19
|
+
val surfaceId = 1
|
|
20
|
+
|
|
21
|
+
beforeTest {
|
|
22
|
+
mockDispatcher = mockk(relaxed = true)
|
|
23
|
+
mockWritableMap = mockk(relaxed = true)
|
|
24
|
+
|
|
25
|
+
// Mock static Arguments.createMap() to return our mock
|
|
26
|
+
mockkStatic(Arguments::class)
|
|
27
|
+
every { Arguments.createMap() } returns mockWritableMap
|
|
28
|
+
|
|
29
|
+
// Mock getSurfaceId() to return test surfaceId
|
|
30
|
+
every { mockDispatcher.getSurfaceId() } returns surfaceId
|
|
31
|
+
|
|
32
|
+
adapter = VideoEventListenerAdapter(
|
|
33
|
+
dispatcher = mockDispatcher,
|
|
34
|
+
viewIdProvider = { viewId }
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
afterTest {
|
|
39
|
+
clearAllMocks()
|
|
40
|
+
unmockkStatic(Arguments::class)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ============================================================
|
|
44
|
+
// onLoadStart Tests
|
|
45
|
+
// ============================================================
|
|
46
|
+
|
|
47
|
+
test("onLoadStart should dispatch topVideoLoadStart event") {
|
|
48
|
+
adapter.onLoadStart(true, "mp4", "https://example.com/video.mp4")
|
|
49
|
+
|
|
50
|
+
verify {
|
|
51
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoLoadStartEvent> {
|
|
52
|
+
it.getEventName() == "topVideoLoadStart" && it.viewTag == viewId
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ============================================================
|
|
58
|
+
// onLoad Tests
|
|
59
|
+
// ============================================================
|
|
60
|
+
|
|
61
|
+
test("onLoad should dispatch topVideoLoad event") {
|
|
62
|
+
val data = GraniteVideoLoadData(
|
|
63
|
+
currentTime = 0.0,
|
|
64
|
+
duration = 60.0,
|
|
65
|
+
naturalWidth = 1920.0,
|
|
66
|
+
naturalHeight = 1080.0,
|
|
67
|
+
orientation = "landscape"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
adapter.onLoad(data)
|
|
71
|
+
|
|
72
|
+
verify {
|
|
73
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoLoadEvent> {
|
|
74
|
+
it.getEventName() == "topVideoLoad" && it.viewTag == viewId
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================================================
|
|
80
|
+
// onError Tests
|
|
81
|
+
// ============================================================
|
|
82
|
+
|
|
83
|
+
test("onError should dispatch topVideoError event") {
|
|
84
|
+
val error = GraniteVideoErrorData(
|
|
85
|
+
code = 1001,
|
|
86
|
+
domain = "ExoPlayer",
|
|
87
|
+
localizedDescription = "Test error",
|
|
88
|
+
errorString = "ERROR_TEST"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
adapter.onError(error)
|
|
92
|
+
|
|
93
|
+
verify {
|
|
94
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoErrorEvent> {
|
|
95
|
+
it.getEventName() == "topVideoError" && it.viewTag == viewId
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ============================================================
|
|
101
|
+
// onProgress Tests
|
|
102
|
+
// ============================================================
|
|
103
|
+
|
|
104
|
+
test("onProgress should dispatch topVideoProgress event") {
|
|
105
|
+
val data = GraniteVideoProgressData(
|
|
106
|
+
currentTime = 10.0,
|
|
107
|
+
playableDuration = 20.0,
|
|
108
|
+
seekableDuration = 60.0
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
adapter.onProgress(data)
|
|
112
|
+
|
|
113
|
+
verify {
|
|
114
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoProgressEvent> {
|
|
115
|
+
it.getEventName() == "topVideoProgress" && it.viewTag == viewId
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ============================================================
|
|
121
|
+
// onSeek Tests
|
|
122
|
+
// ============================================================
|
|
123
|
+
|
|
124
|
+
test("onSeek should dispatch topVideoSeek event") {
|
|
125
|
+
adapter.onSeek(5.0, 10.0)
|
|
126
|
+
|
|
127
|
+
verify {
|
|
128
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoSeekEvent> {
|
|
129
|
+
it.getEventName() == "topVideoSeek" && it.viewTag == viewId
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ============================================================
|
|
135
|
+
// onEnd Tests
|
|
136
|
+
// ============================================================
|
|
137
|
+
|
|
138
|
+
test("onEnd should dispatch topVideoEnd event") {
|
|
139
|
+
adapter.onEnd()
|
|
140
|
+
|
|
141
|
+
verify {
|
|
142
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoEndEvent> {
|
|
143
|
+
it.getEventName() == "topVideoEnd" && it.viewTag == viewId
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ============================================================
|
|
149
|
+
// onBuffer Tests
|
|
150
|
+
// ============================================================
|
|
151
|
+
|
|
152
|
+
test("onBuffer should dispatch topVideoBuffer event") {
|
|
153
|
+
adapter.onBuffer(true)
|
|
154
|
+
|
|
155
|
+
verify {
|
|
156
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoBufferEvent> {
|
|
157
|
+
it.getEventName() == "topVideoBuffer" && it.viewTag == viewId
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ============================================================
|
|
163
|
+
// onBandwidthUpdate Tests
|
|
164
|
+
// ============================================================
|
|
165
|
+
|
|
166
|
+
test("onBandwidthUpdate should dispatch topVideoBandwidthUpdate event") {
|
|
167
|
+
adapter.onBandwidthUpdate(5000000.0, 1920, 1080)
|
|
168
|
+
|
|
169
|
+
verify {
|
|
170
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoBandwidthUpdateEvent> {
|
|
171
|
+
it.getEventName() == "topVideoBandwidthUpdate" && it.viewTag == viewId
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================================
|
|
177
|
+
// onPlaybackStateChanged Tests
|
|
178
|
+
// ============================================================
|
|
179
|
+
|
|
180
|
+
test("onPlaybackStateChanged should dispatch topVideoPlaybackStateChanged event") {
|
|
181
|
+
adapter.onPlaybackStateChanged(true, false, false)
|
|
182
|
+
|
|
183
|
+
verify {
|
|
184
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoPlaybackStateChangedEvent> {
|
|
185
|
+
it.getEventName() == "topVideoPlaybackStateChanged" && it.viewTag == viewId
|
|
186
|
+
})
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ============================================================
|
|
191
|
+
// onPlaybackRateChange Tests
|
|
192
|
+
// ============================================================
|
|
193
|
+
|
|
194
|
+
test("onPlaybackRateChange should dispatch topVideoPlaybackRateChange event") {
|
|
195
|
+
adapter.onPlaybackRateChange(1.5f)
|
|
196
|
+
|
|
197
|
+
verify {
|
|
198
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoPlaybackRateChangeEvent> {
|
|
199
|
+
it.getEventName() == "topVideoPlaybackRateChange" && it.viewTag == viewId
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================================
|
|
205
|
+
// onVolumeChange Tests
|
|
206
|
+
// ============================================================
|
|
207
|
+
|
|
208
|
+
test("onVolumeChange should dispatch topVideoVolumeChange event") {
|
|
209
|
+
adapter.onVolumeChange(0.5f)
|
|
210
|
+
|
|
211
|
+
verify {
|
|
212
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoVolumeChangeEvent> {
|
|
213
|
+
it.getEventName() == "topVideoVolumeChange" && it.viewTag == viewId
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ============================================================
|
|
219
|
+
// onIdle Tests
|
|
220
|
+
// ============================================================
|
|
221
|
+
|
|
222
|
+
test("onIdle should dispatch topVideoIdle event") {
|
|
223
|
+
adapter.onIdle()
|
|
224
|
+
|
|
225
|
+
verify {
|
|
226
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoIdleEvent> {
|
|
227
|
+
it.getEventName() == "topVideoIdle" && it.viewTag == viewId
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ============================================================
|
|
233
|
+
// onReadyForDisplay Tests
|
|
234
|
+
// ============================================================
|
|
235
|
+
|
|
236
|
+
test("onReadyForDisplay should dispatch topVideoReadyForDisplay event") {
|
|
237
|
+
adapter.onReadyForDisplay()
|
|
238
|
+
|
|
239
|
+
verify {
|
|
240
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoReadyForDisplayEvent> {
|
|
241
|
+
it.getEventName() == "topVideoReadyForDisplay" && it.viewTag == viewId
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ============================================================
|
|
247
|
+
// Fullscreen Events Tests
|
|
248
|
+
// ============================================================
|
|
249
|
+
|
|
250
|
+
test("onFullscreenPlayerWillPresent should dispatch topVideoFullscreenPlayerWillPresent event") {
|
|
251
|
+
adapter.onFullscreenPlayerWillPresent()
|
|
252
|
+
|
|
253
|
+
verify {
|
|
254
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoFullscreenPlayerWillPresentEvent> {
|
|
255
|
+
it.getEventName() == "topVideoFullscreenPlayerWillPresent" && it.viewTag == viewId
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
test("onFullscreenPlayerDidPresent should dispatch topVideoFullscreenPlayerDidPresent event") {
|
|
261
|
+
adapter.onFullscreenPlayerDidPresent()
|
|
262
|
+
|
|
263
|
+
verify {
|
|
264
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoFullscreenPlayerDidPresentEvent> {
|
|
265
|
+
it.getEventName() == "topVideoFullscreenPlayerDidPresent" && it.viewTag == viewId
|
|
266
|
+
})
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ============================================================
|
|
271
|
+
// PiP Tests
|
|
272
|
+
// ============================================================
|
|
273
|
+
|
|
274
|
+
test("onPictureInPictureStatusChanged should dispatch topVideoPictureInPictureStatusChanged event") {
|
|
275
|
+
adapter.onPictureInPictureStatusChanged(true)
|
|
276
|
+
|
|
277
|
+
verify {
|
|
278
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoPictureInPictureStatusChangedEvent> {
|
|
279
|
+
it.getEventName() == "topVideoPictureInPictureStatusChanged" && it.viewTag == viewId
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ============================================================
|
|
285
|
+
// Aspect Ratio Tests
|
|
286
|
+
// ============================================================
|
|
287
|
+
|
|
288
|
+
test("onAspectRatioChanged should dispatch topVideoAspectRatio event") {
|
|
289
|
+
adapter.onAspectRatioChanged(16.0, 9.0)
|
|
290
|
+
|
|
291
|
+
verify {
|
|
292
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoAspectRatioEvent> {
|
|
293
|
+
it.getEventName() == "topVideoAspectRatio" && it.viewTag == viewId
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ============================================================
|
|
299
|
+
// Dynamic viewId Tests
|
|
300
|
+
// ============================================================
|
|
301
|
+
|
|
302
|
+
test("viewId changes dynamically should use updated viewId for each dispatch") {
|
|
303
|
+
var currentViewId = 100
|
|
304
|
+
|
|
305
|
+
val dynamicAdapter = VideoEventListenerAdapter(
|
|
306
|
+
dispatcher = mockDispatcher,
|
|
307
|
+
viewIdProvider = { currentViewId }
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
dynamicAdapter.onEnd()
|
|
311
|
+
currentViewId = 200
|
|
312
|
+
dynamicAdapter.onEnd()
|
|
313
|
+
|
|
314
|
+
verifyOrder {
|
|
315
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoEndEvent> { it.viewTag == 100 })
|
|
316
|
+
mockDispatcher.dispatchEvent(match<GraniteVideoEndEvent> { it.viewTag == 200 })
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
})
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
package run.granite.video.helpers
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import io.mockk.mockk
|
|
6
|
+
import run.granite.video.provider.*
|
|
7
|
+
|
|
8
|
+
class FakeGraniteVideoProvider(
|
|
9
|
+
override val providerId: String = "fake",
|
|
10
|
+
override val providerName: String = "Fake Provider"
|
|
11
|
+
) : GraniteVideoProvider {
|
|
12
|
+
|
|
13
|
+
override var delegate: GraniteVideoDelegate? = null
|
|
14
|
+
|
|
15
|
+
var playCount = 0
|
|
16
|
+
private set
|
|
17
|
+
var pauseCount = 0
|
|
18
|
+
private set
|
|
19
|
+
var seekCount = 0
|
|
20
|
+
private set
|
|
21
|
+
var lastLoadedSource: GraniteVideoSource? = null
|
|
22
|
+
private set
|
|
23
|
+
var lastSeekTime: Double = 0.0
|
|
24
|
+
private set
|
|
25
|
+
|
|
26
|
+
private var _isPlaying = false
|
|
27
|
+
private var _currentTime = 0.0
|
|
28
|
+
private var _duration = 0.0
|
|
29
|
+
private var _volume = 1.0f
|
|
30
|
+
private var _muted = false
|
|
31
|
+
private var _rate = 1.0f
|
|
32
|
+
private var _repeat = false
|
|
33
|
+
|
|
34
|
+
override val currentTime: Double get() = _currentTime
|
|
35
|
+
override val duration: Double get() = _duration
|
|
36
|
+
override val isPlaying: Boolean get() = _isPlaying
|
|
37
|
+
|
|
38
|
+
override fun createPlayerView(context: Context): View = mockk(relaxed = true)
|
|
39
|
+
|
|
40
|
+
override fun loadSource(source: GraniteVideoSource) {
|
|
41
|
+
lastLoadedSource = source
|
|
42
|
+
_duration = 0.0
|
|
43
|
+
_currentTime = source.startPosition
|
|
44
|
+
delegate?.onLoadStart(
|
|
45
|
+
isNetwork = source.uri?.startsWith("http") == true,
|
|
46
|
+
type = source.type ?: "unknown",
|
|
47
|
+
uri = source.uri ?: ""
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override fun unload() {
|
|
52
|
+
_isPlaying = false
|
|
53
|
+
_currentTime = 0.0
|
|
54
|
+
_duration = 0.0
|
|
55
|
+
lastLoadedSource = null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override fun play() {
|
|
59
|
+
playCount++
|
|
60
|
+
_isPlaying = true
|
|
61
|
+
delegate?.onPlaybackStateChanged(isPlaying = true, isSeeking = false, isLooping = _repeat)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override fun pause() {
|
|
65
|
+
pauseCount++
|
|
66
|
+
_isPlaying = false
|
|
67
|
+
delegate?.onPlaybackStateChanged(isPlaying = false, isSeeking = false, isLooping = _repeat)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
override fun seek(time: Double, tolerance: Double) {
|
|
71
|
+
seekCount++
|
|
72
|
+
lastSeekTime = time
|
|
73
|
+
val previousTime = _currentTime
|
|
74
|
+
_currentTime = time
|
|
75
|
+
delegate?.onSeek(currentTime = previousTime, seekTime = time)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override fun setVolume(volume: Float) {
|
|
79
|
+
_volume = volume
|
|
80
|
+
delegate?.onVolumeChange(volume)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
override fun setMuted(muted: Boolean) {
|
|
84
|
+
_muted = muted
|
|
85
|
+
delegate?.onVolumeChange(if (muted) 0f else _volume)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
override fun setRate(rate: Float) {
|
|
89
|
+
_rate = rate
|
|
90
|
+
delegate?.onPlaybackRateChange(rate)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override fun setRepeat(shouldRepeat: Boolean) {
|
|
94
|
+
_repeat = shouldRepeat
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Test helpers
|
|
98
|
+
fun simulateLoad(duration: Double, width: Double = 1920.0, height: Double = 1080.0) {
|
|
99
|
+
_duration = duration
|
|
100
|
+
delegate?.onLoad(
|
|
101
|
+
GraniteVideoLoadData(
|
|
102
|
+
currentTime = _currentTime,
|
|
103
|
+
duration = duration,
|
|
104
|
+
naturalWidth = width,
|
|
105
|
+
naturalHeight = height,
|
|
106
|
+
orientation = if (width > height) "landscape" else "portrait"
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fun simulateProgress(currentTime: Double) {
|
|
112
|
+
_currentTime = currentTime
|
|
113
|
+
delegate?.onProgress(
|
|
114
|
+
GraniteVideoProgressData(
|
|
115
|
+
currentTime = currentTime,
|
|
116
|
+
playableDuration = _duration,
|
|
117
|
+
seekableDuration = _duration
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fun simulateEnd() {
|
|
123
|
+
_isPlaying = false
|
|
124
|
+
delegate?.onEnd()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fun simulateError(code: Int, message: String) {
|
|
128
|
+
delegate?.onError(
|
|
129
|
+
GraniteVideoErrorData(
|
|
130
|
+
code = code,
|
|
131
|
+
domain = "Fake",
|
|
132
|
+
localizedDescription = message,
|
|
133
|
+
errorString = message
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fun simulateBuffer(isBuffering: Boolean) {
|
|
139
|
+
delegate?.onBuffer(isBuffering)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override fun release() {
|
|
143
|
+
reset()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
fun reset() {
|
|
147
|
+
playCount = 0
|
|
148
|
+
pauseCount = 0
|
|
149
|
+
seekCount = 0
|
|
150
|
+
lastLoadedSource = null
|
|
151
|
+
lastSeekTime = 0.0
|
|
152
|
+
_isPlaying = false
|
|
153
|
+
_currentTime = 0.0
|
|
154
|
+
_duration = 0.0
|
|
155
|
+
_volume = 1.0f
|
|
156
|
+
_muted = false
|
|
157
|
+
_rate = 1.0f
|
|
158
|
+
_repeat = false
|
|
159
|
+
delegate = null
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package run.granite.video.helpers
|
|
2
|
+
|
|
3
|
+
import run.granite.video.provider.media3.scheduler.ProgressScheduler
|
|
4
|
+
|
|
5
|
+
class TestProgressScheduler : ProgressScheduler {
|
|
6
|
+
private var action: (() -> Unit)? = null
|
|
7
|
+
private var _intervalMs: Long = 0
|
|
8
|
+
|
|
9
|
+
var scheduledCount = 0
|
|
10
|
+
private set
|
|
11
|
+
var cancelCount = 0
|
|
12
|
+
private set
|
|
13
|
+
|
|
14
|
+
val intervalMs: Long get() = _intervalMs
|
|
15
|
+
val isScheduled: Boolean get() = action != null
|
|
16
|
+
|
|
17
|
+
override fun schedule(intervalMs: Long, action: () -> Unit) {
|
|
18
|
+
this.action = action
|
|
19
|
+
this._intervalMs = intervalMs
|
|
20
|
+
scheduledCount++
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override fun cancel() {
|
|
24
|
+
action = null
|
|
25
|
+
cancelCount++
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fun tick() {
|
|
29
|
+
action?.invoke()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fun tick(times: Int) {
|
|
33
|
+
repeat(times) { tick() }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun reset() {
|
|
37
|
+
action = null
|
|
38
|
+
_intervalMs = 0
|
|
39
|
+
scheduledCount = 0
|
|
40
|
+
cancelCount = 0
|
|
41
|
+
}
|
|
42
|
+
}
|