expo-libvlc-player 0.1.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.
Files changed (70) hide show
  1. package/.eslintrc.js +2 -0
  2. package/README.md +107 -0
  3. package/android/.gradle/9.0-milestone-1/checksums/checksums.lock +0 -0
  4. package/android/.gradle/9.0-milestone-1/fileChanges/last-build.bin +0 -0
  5. package/android/.gradle/9.0-milestone-1/fileHashes/fileHashes.lock +0 -0
  6. package/android/.gradle/9.0-milestone-1/gc.properties +0 -0
  7. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  8. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  9. package/android/.gradle/config.properties +2 -0
  10. package/android/.gradle/ideaInitScripts/ijtgtmapper.gradle +3 -0
  11. package/android/.gradle/vcs-1/gc.properties +0 -0
  12. package/android/.idea/AndroidProjectSystem.xml +6 -0
  13. package/android/.idea/android.iml +9 -0
  14. package/android/.idea/caches/deviceStreaming.xml +835 -0
  15. package/android/.idea/deviceManager.xml +13 -0
  16. package/android/.idea/gradle.xml +13 -0
  17. package/android/.idea/misc.xml +3 -0
  18. package/android/.idea/modules.xml +8 -0
  19. package/android/.idea/runConfigurations.xml +17 -0
  20. package/android/.idea/vcs.xml +6 -0
  21. package/android/.idea/workspace.xml +72 -0
  22. package/android/build.gradle +53 -0
  23. package/android/local.properties +8 -0
  24. package/android/proguard-rules.pro +19 -0
  25. package/android/src/main/AndroidManifest.xml +4 -0
  26. package/android/src/main/java/expo/modules/libvlcplayer/AudioFocusManager.kt +210 -0
  27. package/android/src/main/java/expo/modules/libvlcplayer/VlcPlayerManager.kt +82 -0
  28. package/android/src/main/java/expo/modules/libvlcplayer/VlcPlayerModule.kt +129 -0
  29. package/android/src/main/java/expo/modules/libvlcplayer/VlcPlayerView.kt +328 -0
  30. package/android/src/main/java/expo/modules/libvlcplayer/enums/AudioMixingMode.kt +20 -0
  31. package/app.plugin.js +1 -0
  32. package/build/VlcPlayer.types.d.ts +278 -0
  33. package/build/VlcPlayer.types.d.ts.map +1 -0
  34. package/build/VlcPlayer.types.js +2 -0
  35. package/build/VlcPlayer.types.js.map +1 -0
  36. package/build/VlcPlayerModule.d.ts +6 -0
  37. package/build/VlcPlayerModule.d.ts.map +1 -0
  38. package/build/VlcPlayerModule.js +4 -0
  39. package/build/VlcPlayerModule.js.map +1 -0
  40. package/build/VlcPlayerView.d.ts +5 -0
  41. package/build/VlcPlayerView.d.ts.map +1 -0
  42. package/build/VlcPlayerView.js +36 -0
  43. package/build/VlcPlayerView.js.map +1 -0
  44. package/build/index.d.ts +4 -0
  45. package/build/index.d.ts.map +1 -0
  46. package/build/index.js +5 -0
  47. package/build/index.js.map +1 -0
  48. package/build/utils/props.d.ts +3 -0
  49. package/build/utils/props.d.ts.map +1 -0
  50. package/build/utils/props.js +11 -0
  51. package/build/utils/props.js.map +1 -0
  52. package/eslint.config.js +50 -0
  53. package/expo-module.config.json +16 -0
  54. package/ios/Enums/AudioMixingMode.swift +35 -0
  55. package/ios/ExpoLibVlcPlayer.podspec +29 -0
  56. package/ios/VlcPlayerManager.swift +116 -0
  57. package/ios/VlcPlayerModule.swift +110 -0
  58. package/ios/VlcPlayerView.swift +321 -0
  59. package/package.json +49 -0
  60. package/plugin/build/withExpoLibVlcPlayer.d.ts +3 -0
  61. package/plugin/build/withExpoLibVlcPlayer.js +18 -0
  62. package/plugin/src/withExpoLibVlcPlayer.ts +21 -0
  63. package/plugin/tsconfig.json +9 -0
  64. package/plugin/tsconfig.tsbuildinfo +1 -0
  65. package/src/VlcPlayer.types.ts +291 -0
  66. package/src/VlcPlayerModule.ts +7 -0
  67. package/src/VlcPlayerView.tsx +73 -0
  68. package/src/index.ts +4 -0
  69. package/src/utils/props.ts +20 -0
  70. package/tsconfig.json +9 -0
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="DeviceTable">
4
+ <option name="columnSorters">
5
+ <list>
6
+ <ColumnSorterState>
7
+ <option name="column" value="Name" />
8
+ <option name="order" value="ASCENDING" />
9
+ </ColumnSorterState>
10
+ </list>
11
+ </option>
12
+ </component>
13
+ </project>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="GradleMigrationSettings" migrationVersion="1" />
4
+ <component name="GradleSettings">
5
+ <option name="linkedExternalProjectsSettings">
6
+ <GradleProjectSettings>
7
+ <option name="testRunner" value="CHOOSE_PER_TEST" />
8
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
9
+ <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
10
+ </GradleProjectSettings>
11
+ </option>
12
+ </component>
13
+ </project>
@@ -0,0 +1,3 @@
1
+ <project version="4">
2
+ <component name="ExternalStorageConfigurationManager" enabled="true" />
3
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/android.iml" filepath="$PROJECT_DIR$/.idea/android.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="RunConfigurationProducerService">
4
+ <option name="ignoredProducers">
5
+ <set>
6
+ <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
7
+ <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
8
+ <option value="com.intellij.execution.junit.PatternConfigurationProducer" />
9
+ <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
10
+ <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
11
+ <option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
12
+ <option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
13
+ <option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
14
+ </set>
15
+ </option>
16
+ </component>
17
+ </project>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,72 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="AutoImportSettings">
4
+ <option name="autoReloadType" value="NONE" />
5
+ </component>
6
+ <component name="ChangeListManager">
7
+ <list default="true" id="ce1cf2d7-3e2c-41b3-ae06-62438ddceca7" name="Changes" comment="" />
8
+ <option name="SHOW_DIALOG" value="false" />
9
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
11
+ <option name="LAST_RESOLUTION" value="IGNORE" />
12
+ </component>
13
+ <component name="ClangdSettings">
14
+ <option name="formatViaClangd" value="false" />
15
+ </component>
16
+ <component name="ExternalProjectsManager">
17
+ <system id="GRADLE">
18
+ <state>
19
+ <projects_view>
20
+ <tree_state>
21
+ <expand />
22
+ <select />
23
+ </tree_state>
24
+ </projects_view>
25
+ </state>
26
+ </system>
27
+ </component>
28
+ <component name="FormatOnSaveOptions">
29
+ <option name="myRunOnSave" value="true" />
30
+ </component>
31
+ <component name="Git.Settings">
32
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
33
+ </component>
34
+ <component name="ProblemsViewState">
35
+ <option name="selectedTabId" value="ProjectErrors" />
36
+ </component>
37
+ <component name="ProjectColorInfo">{
38
+ &quot;associatedIndex&quot;: 0
39
+ }</component>
40
+ <component name="ProjectId" id="2z2OVb2mI7QaoiYFjJWlUUfJ6HB" />
41
+ <component name="ProjectViewState">
42
+ <option name="hideEmptyMiddlePackages" value="true" />
43
+ <option name="showLibraryContents" value="true" />
44
+ </component>
45
+ <component name="PropertiesComponent"><![CDATA[{
46
+ "keyToString": {
47
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
48
+ "RunOnceActivity.ShowReadmeOnStart": "true",
49
+ "RunOnceActivity.cidr.known.project.marker": "true",
50
+ "RunOnceActivity.git.unshallow": "true",
51
+ "RunOnceActivity.readMode.enableVisualFormatting": "true",
52
+ "android.gradle.sync.needed": "true",
53
+ "cf.first.check.clang-format": "false",
54
+ "cidr.known.project.marker": "true",
55
+ "git-widget-placeholder": "0.1.0",
56
+ "ignore.virus.scanning.warn.message": "true",
57
+ "kotlin-language-version-configured": "true",
58
+ "last_opened_file_path": "//wsl.localhost/Ubuntu/home/rarbit/dev/expo-libvlc-player/android",
59
+ "settings.editor.selected.configurable": "preferences.sourceCode"
60
+ }
61
+ }]]></component>
62
+ <component name="TaskManager">
63
+ <task active="true" id="Default" summary="Default task">
64
+ <changelist id="ce1cf2d7-3e2c-41b3-ae06-62438ddceca7" name="Changes" comment="" />
65
+ <created>1750925729512</created>
66
+ <option name="number" value="Default" />
67
+ <option name="presentableId" value="Default" />
68
+ <updated>1750925729512</updated>
69
+ </task>
70
+ <servers />
71
+ </component>
72
+ </project>
@@ -0,0 +1,53 @@
1
+ apply plugin: 'com.android.library'
2
+
3
+ group = 'expo.modules.libvlcplayer'
4
+ version = '0.1.0'
5
+
6
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
+ apply from: expoModulesCorePlugin
8
+ applyKotlinExpoModulesCorePlugin()
9
+ useCoreDependencies()
10
+ useExpoPublishing()
11
+
12
+ // If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
13
+ // The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
14
+ // Most of the time, you may like to manage the Android SDK versions yourself.
15
+ def useManagedAndroidSdkVersions = false
16
+ if (useManagedAndroidSdkVersions) {
17
+ useDefaultAndroidSdkVersions()
18
+ } else {
19
+ buildscript {
20
+ // Simple helper that allows the root project to override versions declared by this library.
21
+ ext.safeExtGet = { prop, fallback ->
22
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
23
+ }
24
+ }
25
+ project.android {
26
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
27
+ defaultConfig {
28
+ minSdkVersion safeExtGet("minSdkVersion", 21)
29
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
30
+ }
31
+ }
32
+ }
33
+
34
+ android {
35
+ namespace "expo.modules.libvlcplayer"
36
+ defaultConfig {
37
+ versionCode 1
38
+ versionName "0.1.0"
39
+ consumerProguardFiles("proguard-rules.pro")
40
+ }
41
+ lintOptions {
42
+ abortOnError false
43
+ }
44
+ }
45
+
46
+ repositories {
47
+ mavenCentral()
48
+ }
49
+
50
+ dependencies {
51
+ implementation 'com.facebook.react:react-native:+'
52
+ implementation 'org.videolan.android:libvlc-all:3.6.2'
53
+ }
@@ -0,0 +1,8 @@
1
+ ## This file must *NOT* be checked into Version Control Systems,
2
+ # as it contains information specific to your local configuration.
3
+ #
4
+ # Location of the SDK. This is only used by Gradle.
5
+ # For customization when using a Version Control System, please read the
6
+ # header note.
7
+ #Thu Jun 26 02:15:26 CST 2025
8
+ sdk.dir=C\:\\Users\\rarbit\\AppData\\Local\\Android\\Sdk
@@ -0,0 +1,19 @@
1
+ # Add project specific ProGuard rules here.
2
+ # By default, the flags in this file are appended to flags specified
3
+ # in /home/tom/work/android/sdk/tools/proguard/proguard-android.txt
4
+ # You can edit the include path and order by changing the proguardFiles
5
+ # directive in build.gradle.
6
+ #
7
+ # For more details, see
8
+ # http://developer.android.com/guide/developing/tools/proguard.html
9
+
10
+ # Add any project specific keep options here:
11
+
12
+ # If your project uses WebView with JS, uncomment the following
13
+ # and specify the fully qualified class name to the JavaScript interface
14
+ # class:
15
+ #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16
+ # public *;
17
+ #}
18
+
19
+ -keep class org.videolan.libvlc.** { *; }
@@ -0,0 +1,4 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <uses-permission android:name="android.permission.INTERNET" />
4
+ </manifest>
@@ -0,0 +1,210 @@
1
+ package expo.modules.libvlcplayer
2
+
3
+ import kotlinx.coroutines.launch
4
+
5
+ import java.lang.ref.WeakReference
6
+
7
+ import expo.modules.libvlcplayer.enums.AudioMixingMode
8
+
9
+ import android.content.Context
10
+ import android.media.AudioAttributes
11
+ import android.media.AudioFocusRequest
12
+ import android.media.AudioManager
13
+ import android.os.Build
14
+
15
+ import expo.modules.kotlin.AppContext
16
+ import expo.modules.kotlin.exception.CodedException
17
+ import expo.modules.kotlin.types.Enumerable
18
+
19
+ import org.videolan.libvlc.MediaPlayer
20
+
21
+ class AudioFocusManager(
22
+ private val appContext: AppContext,
23
+ private var views: MutableList<WeakReference<VlcPlayerView>>
24
+ ) :
25
+ AudioManager.OnAudioFocusChangeListener {
26
+ private val audioManager by lazy {
27
+ appContext.reactContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
28
+ throw CodedException("Failed to get AudioFocusManager service")
29
+ }
30
+ }
31
+
32
+ private var currentFocusRequest: AudioFocusRequest? = null
33
+ private var currentMixingMode: AudioMixingMode = AudioMixingMode.MIX_WITH_OTHERS
34
+ private val anyPlayerRequiresFocus: Boolean
35
+ get() = views.toList().any { weakView ->
36
+ weakView.get()?.let { view ->
37
+ playerRequiresFocus(view.mediaPlayer)
38
+ } ?: false
39
+ }
40
+
41
+ private var previousVolume: Int = MAX_PLAYER_VOLUME
42
+
43
+ private fun playerRequiresFocus(player: MediaPlayer?): Boolean {
44
+ val mPlayer = player ?: return false
45
+ return mPlayer.isPlaying() && mPlayer.getVolume() > MIN_PLAYER_VOLUME
46
+ }
47
+
48
+ private fun findAudioMixingMode(): AudioMixingMode {
49
+ val mixingModes = views.toList().mapNotNull { view ->
50
+ view.get()?.takeIf { it.mediaPlayer?.isPlaying() == true }?.audioMixingMode
51
+ }
52
+
53
+ if (mixingModes.isEmpty()) {
54
+ return AudioMixingMode.MIX_WITH_OTHERS
55
+ }
56
+
57
+ return mixingModes.reduce { currentAudioMixingMode, next ->
58
+ next.takeIf { it.priority > currentAudioMixingMode.priority } ?: currentAudioMixingMode
59
+ }
60
+ }
61
+
62
+ private fun requestAudioFocus() {
63
+ val audioMixingMode = findAudioMixingMode()
64
+
65
+ if (audioMixingMode == AudioMixingMode.MIX_WITH_OTHERS || !anyPlayerRequiresFocus) {
66
+ abandonAudioFocus()
67
+ currentMixingMode = audioMixingMode
68
+ return
69
+ }
70
+
71
+ val audioFocusType = when (currentMixingMode) {
72
+ AudioMixingMode.DUCK_OTHERS -> AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
73
+ AudioMixingMode.AUTO -> AudioManager.AUDIOFOCUS_GAIN
74
+ AudioMixingMode.DO_NOT_MIX -> AudioManager.AUDIOFOCUS_GAIN
75
+ else -> AudioManager.AUDIOFOCUS_GAIN
76
+ }
77
+
78
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
79
+ currentFocusRequest?.let {
80
+ if (it.focusGain == audioFocusType) {
81
+ return
82
+ }
83
+ }
84
+
85
+ val newFocusRequest = AudioFocusRequest.Builder(audioFocusType).run {
86
+ setAudioAttributes(
87
+ AudioAttributes.Builder().run {
88
+ setUsage(AudioAttributes.USAGE_MEDIA)
89
+ setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
90
+ setOnAudioFocusChangeListener(this@AudioFocusManager)
91
+ build()
92
+ }
93
+ ).build()
94
+ }
95
+
96
+ currentFocusRequest = newFocusRequest
97
+ audioManager.requestAudioFocus(newFocusRequest)
98
+ } else {
99
+ @Suppress("DEPRECATION")
100
+ audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, audioFocusType)
101
+ }
102
+
103
+ currentMixingMode = audioMixingMode
104
+ }
105
+
106
+ private fun abandonAudioFocus() {
107
+ currentFocusRequest?.let {
108
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
109
+ audioManager.abandonAudioFocusRequest(it)
110
+ } else {
111
+ @Suppress("DEPRECATION")
112
+ audioManager.abandonAudioFocus(this)
113
+ }
114
+ }
115
+
116
+ currentFocusRequest = null
117
+ }
118
+
119
+ fun updateAudioFocus() {
120
+ if (anyPlayerRequiresFocus || findAudioMixingMode() != currentMixingMode) {
121
+ requestAudioFocus()
122
+ } else {
123
+ abandonAudioFocus()
124
+ }
125
+ }
126
+
127
+ override fun onAudioFocusChange(focusChange: Int) {
128
+ when (focusChange) {
129
+ AudioManager.AUDIOFOCUS_LOSS -> {
130
+ appContext.mainQueue.launch {
131
+ views.forEach { view ->
132
+ pausePlayerIfUnmuted(view.get()?.mediaPlayer)
133
+ }
134
+
135
+ currentFocusRequest = null
136
+ }
137
+ }
138
+
139
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
140
+ val audioMixingMode = findAudioMixingMode()
141
+
142
+ if (audioMixingMode == AudioMixingMode.MIX_WITH_OTHERS) {
143
+ return
144
+ }
145
+
146
+ appContext.mainQueue.launch {
147
+ views.forEach { view ->
148
+ pausePlayerIfUnmuted(view.get()?.mediaPlayer)
149
+ }
150
+
151
+ currentFocusRequest = null
152
+ }
153
+ }
154
+
155
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
156
+ val audioMixingMode = findAudioMixingMode()
157
+
158
+ appContext.mainQueue.launch {
159
+ views.forEach { view ->
160
+ view.get()?.mediaPlayer?.let { player ->
161
+ if (audioMixingMode == AudioMixingMode.DO_NOT_MIX) {
162
+ pausePlayerIfUnmuted(player)
163
+ } else {
164
+ duckPlayer(player)
165
+ }
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ AudioManager.AUDIOFOCUS_GAIN -> {
172
+ appContext.mainQueue.launch {
173
+ views.forEach { view ->
174
+ unduckPlayer(view.get()?.mediaPlayer)
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ private fun pausePlayerIfUnmuted(player: MediaPlayer?) {
182
+ player?.let { mediaPlayer ->
183
+ if (mediaPlayer.getVolume() > MIN_PLAYER_VOLUME) {
184
+ appContext.mainQueue.launch {
185
+ mediaPlayer.pause()
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ private fun duckPlayer(player: MediaPlayer?) {
192
+ player?.let { weakPlayer ->
193
+ appContext.mainQueue.launch {
194
+ val volume = weakPlayer.getVolume() / 20
195
+ weakPlayer.setVolume(volume)
196
+ previousVolume = volume
197
+ }
198
+ }
199
+ }
200
+
201
+ private fun unduckPlayer(player: MediaPlayer?) {
202
+ player?.let { weakPlayer ->
203
+ if (weakPlayer.getVolume() > MIN_PLAYER_VOLUME) {
204
+ appContext.mainQueue.launch {
205
+ weakPlayer.setVolume(previousVolume)
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
@@ -0,0 +1,82 @@
1
+ package expo.modules.libvlcplayer
2
+
3
+ import java.lang.ref.WeakReference
4
+
5
+ import expo.modules.kotlin.AppContext
6
+ import expo.modules.kotlin.exception.Exceptions
7
+
8
+ import org.videolan.libvlc.MediaPlayer
9
+
10
+ object VlcPlayerManager {
11
+ private var views: MutableList<WeakReference<VlcPlayerView>> = mutableListOf()
12
+
13
+ lateinit var audioFocusManager: AudioFocusManager
14
+
15
+ fun onModuleCreated(appContext: AppContext) {
16
+ val context = appContext.reactContext ?: throw Exceptions.ReactContextLost()
17
+
18
+ if (!this::audioFocusManager.isInitialized) {
19
+ audioFocusManager = AudioFocusManager(appContext, views)
20
+ }
21
+ }
22
+
23
+ fun registerView(view: VlcPlayerView) {
24
+ views.find { it.get() == view } ?: run { views.add(WeakReference(view)) }
25
+ audioFocusManager.updateAudioFocus()
26
+ }
27
+
28
+ fun unregisterView(view: VlcPlayerView) {
29
+ views.removeAll { it.get() == view }
30
+ audioFocusManager.updateAudioFocus()
31
+ }
32
+
33
+ fun onAppDestroyed() {
34
+ views.forEach { view ->
35
+ view.get()?.destroyPlayer()
36
+ }
37
+ }
38
+
39
+ fun onViewDestroyed(view: VlcPlayerView) {
40
+ view.destroyPlayer()
41
+ }
42
+
43
+ fun onAppForegrounded() {
44
+ views.forEach { view ->
45
+ view.get()?.let { playerView ->
46
+ playerView.mediaPlayer?.let { player ->
47
+ player.attachViews(
48
+ playerView.videoLayout,
49
+ null,
50
+ ENABLE_SUBTITLES,
51
+ USE_TEXTURE_VIEW
52
+ )
53
+
54
+ if (!player.isPlaying()) {
55
+ val time = player.getTime()
56
+ val error = (-1).toLong()
57
+
58
+ if (time != error) {
59
+ player.setTime(time)
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ fun onAppBackgrounded() {
68
+ views.forEach { view ->
69
+ view.get()?.let { playerView ->
70
+ playerView.onBackground(mapOf())
71
+
72
+ playerView.mediaPlayer?.let { player ->
73
+ if (!playerView.playInBackground && player.isPlaying()) {
74
+ player.pause()
75
+ }
76
+
77
+ player.detachViews()
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,129 @@
1
+ package expo.modules.libvlcplayer
2
+
3
+ import expo.modules.libvlcplayer.enums.AudioMixingMode
4
+
5
+ import com.facebook.react.bridge.ReadableMap
6
+
7
+ import expo.modules.kotlin.modules.Module
8
+ import expo.modules.kotlin.modules.ModuleDefinition
9
+
10
+ private const val BUFFERING_EVENT = "onBuffering"
11
+ private const val PLAYING_EVENT = "onPlaying"
12
+ private const val PAUSED_EVENT = "onPaused"
13
+ private const val STOPPED_EVENT = "onStopped"
14
+ private const val ENDED_EVENT = "onEnded"
15
+ private const val REPEAT_EVENT = "onRepeat"
16
+ private const val WARN_EVENT = "onWarn"
17
+ private const val ERROR_EVENT = "onError"
18
+ private const val POSITION_CHANGED_EVENT = "onPositionChanged"
19
+ private const val LOAD_EVENT = "onLoad"
20
+ private const val BACKGROUND_EVENT = "onBackground"
21
+
22
+ val playerEvents = arrayOf(
23
+ BUFFERING_EVENT,
24
+ PLAYING_EVENT,
25
+ PAUSED_EVENT,
26
+ STOPPED_EVENT,
27
+ ENDED_EVENT,
28
+ REPEAT_EVENT,
29
+ WARN_EVENT,
30
+ ERROR_EVENT,
31
+ POSITION_CHANGED_EVENT,
32
+ LOAD_EVENT,
33
+ BACKGROUND_EVENT
34
+ )
35
+
36
+ class VlcPlayerModule : Module() {
37
+ override fun definition() = ModuleDefinition {
38
+ Name("ExpoLibVlcPlayer")
39
+
40
+ OnCreate {
41
+ VlcPlayerManager.onModuleCreated(appContext)
42
+ }
43
+
44
+ OnDestroy {
45
+ VlcPlayerManager.onAppDestroyed()
46
+ }
47
+
48
+ View(VlcPlayerView::class) {
49
+ Events(playerEvents)
50
+
51
+ Prop("uri") { view: VlcPlayerView, uri: String ->
52
+ view.uri = uri
53
+ }
54
+
55
+ Prop("subtitle") { view: VlcPlayerView, subtitle: ReadableMap? ->
56
+ view.setSubtitle(subtitle)
57
+ }
58
+
59
+ Prop("options") { view: VlcPlayerView, options: ArrayList<String>? ->
60
+ view.options = options ?: ArrayList<String>()
61
+ }
62
+
63
+ Prop("volume") { view: VlcPlayerView, volume: Int? ->
64
+ view.setVolume(volume ?: MAX_PLAYER_VOLUME)
65
+ }
66
+
67
+ Prop("mute") { view: VlcPlayerView, mute: Boolean? ->
68
+ view.setMute(mute ?: false)
69
+ }
70
+
71
+ Prop("rate") { view: VlcPlayerView, rate: Float? ->
72
+ view.setRate(rate ?: DEFAULT_PLAYER_RATE)
73
+ }
74
+
75
+ Prop("tracks") { view: VlcPlayerView, tracks: ReadableMap? ->
76
+ view.setTracks(tracks)
77
+ }
78
+
79
+ Prop("repeat") { view: VlcPlayerView, repeat: Boolean? ->
80
+ view.setRepeat(repeat ?: false)
81
+ }
82
+
83
+ Prop("aspectRatio") { view: VlcPlayerView, aspectRatio: String? ->
84
+ view.setAspectRatio(aspectRatio)
85
+ }
86
+
87
+ Prop("audioMixingMode") { view: VlcPlayerView, audioMixingMode: AudioMixingMode ->
88
+ view.audioMixingMode = audioMixingMode
89
+ }
90
+
91
+ Prop("playInBackground") { view: VlcPlayerView, playInBackground: Boolean? ->
92
+ view.playInBackground = playInBackground ?: false
93
+ }
94
+
95
+ Prop("autoplay") { view: VlcPlayerView, autoplay: Boolean? ->
96
+ view.setAutoplay(autoplay ?: true)
97
+ }
98
+
99
+ OnViewDestroys { view: VlcPlayerView ->
100
+ VlcPlayerManager.onViewDestroyed(view)
101
+ VlcPlayerManager.unregisterView(view)
102
+ }
103
+
104
+ AsyncFunction("play") { view: VlcPlayerView ->
105
+ view.play()
106
+ }
107
+
108
+ AsyncFunction("pause") { view: VlcPlayerView ->
109
+ view.pause()
110
+ }
111
+
112
+ AsyncFunction("stop") { view: VlcPlayerView ->
113
+ view.stop()
114
+ }
115
+
116
+ AsyncFunction("seek") { view: VlcPlayerView, position: Float ->
117
+ view.seek(position)
118
+ }
119
+ }
120
+
121
+ OnActivityEntersForeground {
122
+ VlcPlayerManager.onAppForegrounded()
123
+ }
124
+
125
+ OnActivityEntersBackground {
126
+ VlcPlayerManager.onAppBackgrounded()
127
+ }
128
+ }
129
+ }