nosnia-audio-recorder 0.4.2 → 0.4.4

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.
@@ -13,11 +13,13 @@ Pod::Spec.new do |s|
13
13
  s.platforms = { :ios => min_ios_version_supported }
14
14
  s.source = { :git => "https://nosnia.ai.git", :tag => "#{s.version}" }
15
15
 
16
- s.source_files = [
17
- "ios/NosniaAudioRecorder.{h,mm}",
18
- "ios/NosniaAudioPlayer.{h,mm}"
19
- ]
16
+ s.source_files = "ios/*.{h,mm}"
17
+ s.exclude_files = "ios/**/*.generated.*"
20
18
  s.frameworks = "AVFoundation", "MediaPlayer"
21
19
 
20
+ # Only install module dependencies, skip Codegen duplication
22
21
  install_modules_dependencies(s)
22
+
23
+ # Prevent duplicate Codegen symbols by using header_mappings_dir
24
+ s.header_mappings_dir = "ios"
23
25
  end
@@ -1,238 +1,238 @@
1
- package com.nosniaaudiorecorder
2
-
3
- import android.media.MediaPlayer
4
- import android.os.Handler
5
- import android.os.Looper
6
- import com.facebook.react.bridge.Promise
7
- import com.facebook.react.bridge.ReactApplicationContext
8
- import com.facebook.react.bridge.ReadableMap
9
- import com.facebook.react.bridge.WritableMap
10
- import com.facebook.react.bridge.WritableNativeMap
11
- import com.facebook.react.bridge.Arguments
12
- import com.facebook.react.modules.core.DeviceEventManagerModule
13
- import com.facebook.react.module.annotations.ReactModule
14
- import java.io.File
15
-
16
- @ReactModule(name = NosniaAudioPlayerModule.NAME)
17
- class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
18
- NativeNosniaAudioPlayerSpec(reactContext) {
19
-
20
- private var mediaPlayer: MediaPlayer? = null
21
- private var currentFilePath: String? = null
22
- private var isPlaying = false
23
-
24
- private val progressHandler = Handler(Looper.getMainLooper())
25
- private val progressRunnable = object : Runnable {
26
- override fun run() {
27
- mediaPlayer?.let { player ->
28
- if (isPlaying && player.isPlaying) {
29
- val currentTime = player.currentPosition.toLong()
30
- val duration = player.duration.toLong()
31
-
32
- sendEvent("onPlaybackProgress", Arguments.createMap().apply {
33
- putDouble("currentTime", currentTime.toDouble())
34
- putDouble("duration", duration.toDouble())
35
- putBoolean("isPlaying", true)
36
- })
37
-
38
- progressHandler.postDelayed(this, 100)
39
- }
40
- }
41
- }
42
- }
43
-
44
- private fun sendEvent(eventName: String, params: WritableMap?) {
45
- reactApplicationContext
46
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
47
- .emit(eventName, params)
48
- }
49
-
50
- private fun startProgressUpdates() {
51
- progressHandler.post(progressRunnable)
52
- }
53
-
54
- private fun stopProgressUpdates() {
55
- progressHandler.removeCallbacks(progressRunnable)
56
- }
57
-
58
- override fun getName(): String {
59
- return NAME
60
- }
61
-
62
- override fun startPlaying(options: ReadableMap, promise: Promise) {
63
- try {
64
- val filePath = options.getString("filePath")
65
- if (filePath == null) {
66
- promise.reject("INVALID_PATH", "File path is required")
67
- return
68
- }
69
-
70
- val volume = if (options.hasKey("volume")) {
71
- options.getDouble("volume").toFloat()
72
- } else {
73
- 1.0f
74
- }
75
-
76
- val loop = if (options.hasKey("loop")) {
77
- options.getBoolean("loop")
78
- } else {
79
- false
80
- }
81
-
82
- // Check if file exists
83
- val file = File(filePath)
84
- if (!file.exists()) {
85
- promise.reject("FILE_NOT_FOUND", "Audio file not found: $filePath")
86
- return
87
- }
88
-
89
- // Release existing player if any
90
- mediaPlayer?.release()
91
-
92
- mediaPlayer = MediaPlayer().apply {
93
- setDataSource(filePath)
94
- setVolume(volume, volume)
95
- isLooping = loop
96
-
97
- setOnCompletionListener {
98
- isPlaying = false
99
- stopProgressUpdates()
100
- sendEvent("onPlaybackComplete", Arguments.createMap())
101
- }
102
-
103
- setOnErrorListener { _, what, extra ->
104
- isPlaying = false
105
- stopProgressUpdates()
106
- true
107
- }
108
-
109
- prepare()
110
- start()
111
- }
112
-
113
- isPlaying = true
114
- currentFilePath = filePath
115
- startProgressUpdates()
116
- promise.resolve(null)
117
- } catch (e: Exception) {
118
- mediaPlayer?.release()
119
- mediaPlayer = null
120
- isPlaying = false
121
- promise.reject("START_PLAYING_ERROR", e.message, e)
122
- }
123
- }
124
-
125
- override fun stopPlaying(promise: Promise) {
126
- try {
127
- stopProgressUpdates()
128
-
129
- mediaPlayer?.apply {
130
- if (isPlaying) {
131
- stop()
132
- }
133
- release()
134
- }
135
- mediaPlayer = null
136
- isPlaying = false
137
- currentFilePath = null
138
-
139
- promise.resolve(null)
140
- } catch (e: Exception) {
141
- promise.reject("STOP_PLAYING_ERROR", e.message, e)
142
- }
143
- }
144
-
145
- override fun pausePlaying(promise: Promise) {
146
- try {
147
- if (!isPlaying || mediaPlayer == null) {
148
- promise.reject("NOT_PLAYING", "No playback in progress")
149
- return
150
- }
151
-
152
- mediaPlayer?.pause()
153
- isPlaying = false
154
- promise.resolve(null)
155
- } catch (e: Exception) {
156
- promise.reject("PAUSE_ERROR", e.message, e)
157
- }
158
- }
159
-
160
- override fun resumePlaying(promise: Promise) {
161
- try {
162
- if (isPlaying || mediaPlayer == null) {
163
- promise.reject("NOT_PAUSED", "Playback is not paused")
164
- return
165
- }
166
-
167
- mediaPlayer?.start()
168
- isPlaying = true
169
- promise.resolve(null)
170
- } catch (e: Exception) {
171
- promise.reject("RESUME_ERROR", e.message, e)
172
- }
173
- }
174
-
175
- override fun seekToTime(time: Double, promise: Promise) {
176
- try {
177
- if (mediaPlayer == null) {
178
- promise.reject("NO_PLAYER", "No audio player initialized")
179
- return
180
- }
181
-
182
- val timeMs = (time * 1000).toInt()
183
- mediaPlayer?.seekTo(timeMs)
184
- promise.resolve(null)
185
- } catch (e: Exception) {
186
- promise.reject("SEEK_ERROR", e.message, e)
187
- }
188
- }
189
-
190
- override fun setVolume(volume: Double, promise: Promise) {
191
- try {
192
- if (mediaPlayer == null) {
193
- promise.reject("NO_PLAYER", "No audio player initialized")
194
- return
195
- }
196
-
197
- val volumeFloat = volume.toFloat()
198
- mediaPlayer?.setVolume(volumeFloat, volumeFloat)
199
- promise.resolve(null)
200
- } catch (e: Exception) {
201
- promise.reject("SET_VOLUME_ERROR", e.message, e)
202
- }
203
- }
204
-
205
- override fun getPlayerStatus(promise: Promise) {
206
- try {
207
- val status = WritableNativeMap().apply {
208
- putBoolean("isPlaying", isPlaying)
209
-
210
- if (mediaPlayer != null) {
211
- putDouble("duration", mediaPlayer!!.duration.toDouble())
212
- putDouble("currentTime", mediaPlayer!!.currentPosition.toDouble())
213
- } else {
214
- putDouble("duration", 0.0)
215
- putDouble("currentTime", 0.0)
216
- }
217
-
218
- if (currentFilePath != null) {
219
- putString("currentFilePath", currentFilePath)
220
- }
221
- }
222
- promise.resolve(status)
223
- } catch (e: Exception) {
224
- promise.reject("STATUS_ERROR", e.message, e)
225
- }
226
- }
227
-
228
- override fun onCatalystInstanceDestroy() {
229
- super.onCatalystInstanceDestroy()
230
- stopProgressUpdates()
231
- mediaPlayer?.release()
232
- mediaPlayer = null
233
- }
234
-
235
- companion object {
236
- const val NAME = "NosniaAudioPlayer"
237
- }
238
- }
1
+ package com.nosniaaudiorecorder
2
+
3
+ import android.media.MediaPlayer
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import com.facebook.react.bridge.Promise
7
+ import com.facebook.react.bridge.ReactApplicationContext
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.facebook.react.bridge.WritableMap
10
+ import com.facebook.react.bridge.WritableNativeMap
11
+ import com.facebook.react.bridge.Arguments
12
+ import com.facebook.react.modules.core.DeviceEventManagerModule
13
+ import com.facebook.react.module.annotations.ReactModule
14
+ import java.io.File
15
+
16
+ @ReactModule(name = NosniaAudioPlayerModule.NAME)
17
+ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
18
+ NativeNosniaAudioPlayerSpec(reactContext) {
19
+
20
+ private var mediaPlayer: MediaPlayer? = null
21
+ private var currentFilePath: String? = null
22
+ private var isPlaying = false
23
+
24
+ private val progressHandler = Handler(Looper.getMainLooper())
25
+ private val progressRunnable = object : Runnable {
26
+ override fun run() {
27
+ mediaPlayer?.let { player ->
28
+ if (isPlaying && player.isPlaying) {
29
+ val currentTime = player.currentPosition.toLong()
30
+ val duration = player.duration.toLong()
31
+
32
+ sendEvent("onPlaybackProgress", Arguments.createMap().apply {
33
+ putDouble("currentTime", currentTime.toDouble())
34
+ putDouble("duration", duration.toDouble())
35
+ putBoolean("isPlaying", true)
36
+ })
37
+
38
+ progressHandler.postDelayed(this, 100)
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ private fun sendEvent(eventName: String, params: WritableMap?) {
45
+ reactApplicationContext
46
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
47
+ .emit(eventName, params)
48
+ }
49
+
50
+ private fun startProgressUpdates() {
51
+ progressHandler.post(progressRunnable)
52
+ }
53
+
54
+ private fun stopProgressUpdates() {
55
+ progressHandler.removeCallbacks(progressRunnable)
56
+ }
57
+
58
+ override fun getName(): String {
59
+ return NAME
60
+ }
61
+
62
+ override fun startPlaying(options: ReadableMap, promise: Promise) {
63
+ try {
64
+ val filePath = options.getString("filePath")
65
+ if (filePath == null) {
66
+ promise.reject("INVALID_PATH", "File path is required")
67
+ return
68
+ }
69
+
70
+ val volume = if (options.hasKey("volume")) {
71
+ options.getDouble("volume").toFloat()
72
+ } else {
73
+ 1.0f
74
+ }
75
+
76
+ val loop = if (options.hasKey("loop")) {
77
+ options.getBoolean("loop")
78
+ } else {
79
+ false
80
+ }
81
+
82
+ // Check if file exists
83
+ val file = File(filePath)
84
+ if (!file.exists()) {
85
+ promise.reject("FILE_NOT_FOUND", "Audio file not found: $filePath")
86
+ return
87
+ }
88
+
89
+ // Release existing player if any
90
+ mediaPlayer?.release()
91
+
92
+ mediaPlayer = MediaPlayer().apply {
93
+ setDataSource(filePath)
94
+ setVolume(volume, volume)
95
+ isLooping = loop
96
+
97
+ setOnCompletionListener {
98
+ isPlaying = false
99
+ stopProgressUpdates()
100
+ sendEvent("onPlaybackComplete", Arguments.createMap())
101
+ }
102
+
103
+ setOnErrorListener { _, what, extra ->
104
+ isPlaying = false
105
+ stopProgressUpdates()
106
+ true
107
+ }
108
+
109
+ prepare()
110
+ start()
111
+ }
112
+
113
+ isPlaying = true
114
+ currentFilePath = filePath
115
+ startProgressUpdates()
116
+ promise.resolve(null)
117
+ } catch (e: Exception) {
118
+ mediaPlayer?.release()
119
+ mediaPlayer = null
120
+ isPlaying = false
121
+ promise.reject("START_PLAYING_ERROR", e.message, e)
122
+ }
123
+ }
124
+
125
+ override fun stopPlaying(promise: Promise) {
126
+ try {
127
+ stopProgressUpdates()
128
+
129
+ mediaPlayer?.apply {
130
+ if (isPlaying) {
131
+ stop()
132
+ }
133
+ release()
134
+ }
135
+ mediaPlayer = null
136
+ isPlaying = false
137
+ currentFilePath = null
138
+
139
+ promise.resolve(null)
140
+ } catch (e: Exception) {
141
+ promise.reject("STOP_PLAYING_ERROR", e.message, e)
142
+ }
143
+ }
144
+
145
+ override fun pausePlaying(promise: Promise) {
146
+ try {
147
+ if (!isPlaying || mediaPlayer == null) {
148
+ promise.reject("NOT_PLAYING", "No playback in progress")
149
+ return
150
+ }
151
+
152
+ mediaPlayer?.pause()
153
+ isPlaying = false
154
+ promise.resolve(null)
155
+ } catch (e: Exception) {
156
+ promise.reject("PAUSE_ERROR", e.message, e)
157
+ }
158
+ }
159
+
160
+ override fun resumePlaying(promise: Promise) {
161
+ try {
162
+ if (isPlaying || mediaPlayer == null) {
163
+ promise.reject("NOT_PAUSED", "Playback is not paused")
164
+ return
165
+ }
166
+
167
+ mediaPlayer?.start()
168
+ isPlaying = true
169
+ promise.resolve(null)
170
+ } catch (e: Exception) {
171
+ promise.reject("RESUME_ERROR", e.message, e)
172
+ }
173
+ }
174
+
175
+ override fun seekToTime(time: Double, promise: Promise) {
176
+ try {
177
+ if (mediaPlayer == null) {
178
+ promise.reject("NO_PLAYER", "No audio player initialized")
179
+ return
180
+ }
181
+
182
+ val timeMs = (time * 1000).toInt()
183
+ mediaPlayer?.seekTo(timeMs)
184
+ promise.resolve(null)
185
+ } catch (e: Exception) {
186
+ promise.reject("SEEK_ERROR", e.message, e)
187
+ }
188
+ }
189
+
190
+ override fun setVolume(volume: Double, promise: Promise) {
191
+ try {
192
+ if (mediaPlayer == null) {
193
+ promise.reject("NO_PLAYER", "No audio player initialized")
194
+ return
195
+ }
196
+
197
+ val volumeFloat = volume.toFloat()
198
+ mediaPlayer?.setVolume(volumeFloat, volumeFloat)
199
+ promise.resolve(null)
200
+ } catch (e: Exception) {
201
+ promise.reject("SET_VOLUME_ERROR", e.message, e)
202
+ }
203
+ }
204
+
205
+ override fun getPlayerStatus(promise: Promise) {
206
+ try {
207
+ val status = WritableNativeMap().apply {
208
+ putBoolean("isPlaying", isPlaying)
209
+
210
+ if (mediaPlayer != null) {
211
+ putDouble("duration", mediaPlayer!!.duration.toDouble())
212
+ putDouble("currentTime", mediaPlayer!!.currentPosition.toDouble())
213
+ } else {
214
+ putDouble("duration", 0.0)
215
+ putDouble("currentTime", 0.0)
216
+ }
217
+
218
+ if (currentFilePath != null) {
219
+ putString("currentFilePath", currentFilePath)
220
+ }
221
+ }
222
+ promise.resolve(status)
223
+ } catch (e: Exception) {
224
+ promise.reject("STATUS_ERROR", e.message, e)
225
+ }
226
+ }
227
+
228
+ override fun onCatalystInstanceDestroy() {
229
+ super.onCatalystInstanceDestroy()
230
+ stopProgressUpdates()
231
+ mediaPlayer?.release()
232
+ mediaPlayer = null
233
+ }
234
+
235
+ companion object {
236
+ const val NAME = "NosniaAudioPlayer"
237
+ }
238
+ }
@@ -1,7 +1,7 @@
1
- #import <NosniaAudioPlayerSpec/NosniaAudioPlayerSpec.h>
2
- #import <React/RCTBridgeModule.h>
3
- #import <React/RCTEventEmitter.h>
4
-
5
- @interface NosniaAudioPlayer : RCTEventEmitter <NativeNosniaAudioPlayerSpec>
6
-
7
- @end
1
+ #import <NosniaAudioPlayerSpec/NosniaAudioPlayerSpec.h>
2
+ #import <React/RCTBridgeModule.h>
3
+ #import <React/RCTEventEmitter.h>
4
+
5
+ @interface NosniaAudioPlayer : RCTEventEmitter <NativeNosniaAudioPlayerSpec>
6
+
7
+ @end