@cloudfort/callum-voice 0.0.3 → 0.0.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.
package/README.md CHANGED
@@ -70,7 +70,7 @@ callum-react-native/
70
70
  │ ├── build.gradle # Android build config
71
71
  │ └── src/main/
72
72
  │ ├── AndroidManifest.xml # Permissions
73
- │ └── java/com/callumvoice/
73
+ │ └── java/ir/cloudfort/callum/
74
74
  │ ├── CallumVoiceModule.kt # Native audio module
75
75
  │ └── CallumVoicePackage.kt # Package registration
76
76
  ├── ios/
@@ -136,7 +136,7 @@ cd ios && pod install && cd ..
136
136
  In `MainApplication.kt`:
137
137
 
138
138
  ```kotlin
139
- import com.callumvoice.CallumVoicePackage
139
+ import ir.cloudfort.callum.CallumVoicePackage
140
140
 
141
141
  override fun getPackages(): List<ReactPackage> {
142
142
  return listOf(
@@ -173,7 +173,7 @@ import { VoiceClient, createVoiceConfig } from 'callum-voice';
173
173
  // 1. Create config
174
174
  const config = createVoiceConfig({
175
175
  server: 'callem.cloudfort.ir',
176
- apiKey: 'vc_live_0123456789abcdef0123456789abcdef',
176
+ apiKey: 'vc_token',
177
177
  peerId: 'user_123',
178
178
  });
179
179
 
@@ -236,7 +236,7 @@ export default function VoiceChatScreen() {
236
236
  useEffect(() => {
237
237
  const config = createVoiceConfig({
238
238
  server: 'callem.cloudfort.ir',
239
- apiKey: 'vc_live_0123456789abcdef0123456789abcdef',
239
+ apiKey: 'vc_token',
240
240
  peerId: `user_${Date.now()}`,
241
241
  });
242
242
 
@@ -550,7 +550,7 @@ interface VoiceEventListener {
550
550
 
551
551
  ### Android (Kotlin)
552
552
 
553
- The Android native module at `android/src/main/java/com/callumvoice/CallumVoiceModule.kt` provides:
553
+ The Android native module at `android/src/main/java/ir/cloudfort/callum/CallumVoiceModule.kt` provides:
554
554
 
555
555
  | Method | Description |
556
556
  |--------|-------------|
@@ -8,7 +8,7 @@ apply plugin: 'com.android.library'
8
8
  apply plugin: 'kotlin-android'
9
9
 
10
10
  android {
11
- namespace "com.callumvoice"
11
+ namespace "ir.cloudfort"
12
12
  compileSdkVersion safeExtGet('compileSdkVersion', 34)
13
13
 
14
14
  defaultConfig {
@@ -1,5 +1,5 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.callumvoice">
2
+ package="ir.cloudfort.callum">
3
3
 
4
4
  <uses-permission android:name="android.permission.INTERNET" />
5
5
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
@@ -1,289 +1,289 @@
1
- // Copyright Cloudfort. All Rights Reserved.
2
-
3
- package com.callumvoice
4
-
5
- import android.media.AudioFormat
6
- import android.media.AudioManager
7
- import android.media.AudioRecord
8
- import android.media.AudioTrack
9
- import android.media.MediaRecorder
10
- import android.os.Build
11
- import com.facebook.react.bridge.*
12
- import com.facebook.react.modules.core.DeviceEventManagerModule
13
-
14
- /**
15
- * React Native native module for Android audio capture and playback.
16
- *
17
- * Handles microphone recording (AudioRecord) and audio playback (AudioTrack)
18
- * using PCM 16-bit mono at 48000 Hz. Communicates audio data to/from the
19
- * JavaScript layer via DeviceEventManagerModule events.
20
- */
21
- class CallumVoiceModule(reactContext: ReactApplicationContext)
22
- : ReactContextBaseJavaModule(reactContext) {
23
-
24
- companion object {
25
- private const val MODULE_NAME = "CallumVoice"
26
- private const val SAMPLE_RATE = 48000
27
- private const val FRAME_SIZE = 960 // 20ms at 48kHz
28
-
29
- // JS event names
30
- private const val EVENT_AUDIO_CAPTURED = "onAudioCaptured"
31
- private const val EVENT_AUDIO_ERROR = "onAudioError"
32
- }
33
-
34
- private var audioRecord: AudioRecord? = null
35
- private var audioTrack: AudioTrack? = null
36
- private var captureThread: Thread? = null
37
- private var playbackThread: Thread? = null
38
- private var captureRunning = false
39
- private var playbackRunning = false
40
-
41
- override fun getName(): String = MODULE_NAME
42
-
43
- /**
44
- * Exported constants accessible from JavaScript via NativeModules.CallumVoice.
45
- */
46
- override fun getConstants(): Map<String, Any> {
47
- return hashMapOf(
48
- "SAMPLE_RATE" to SAMPLE_RATE,
49
- "FRAME_SIZE" to FRAME_SIZE
50
- )
51
- }
52
-
53
- // ── Microphone Capture ─────────────────────────────────────────
54
-
55
- @ReactMethod
56
- fun startAudioCapture(promise: Promise) {
57
- try {
58
- val bufferSize = AudioRecord.getMinBufferSize(
59
- SAMPLE_RATE,
60
- AudioFormat.CHANNEL_IN_MONO,
61
- AudioFormat.ENCODING_PCM_16BIT
62
- )
63
-
64
- if (bufferSize == AudioRecord.ERROR || bufferSize == AudioRecord.ERROR_BAD_VALUE) {
65
- promise.reject("AUDIO_ERROR", "Cannot get minimum buffer size")
66
- return
67
- }
68
-
69
- val audioSource = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
70
- MediaRecorder.AudioSource.VOICE_COMMUNICATION
71
- } else {
72
- MediaRecorder.AudioSource.VOICE_COMMUNICATION
73
- }
74
-
75
- audioRecord = AudioRecord(
76
- audioSource,
77
- SAMPLE_RATE,
78
- AudioFormat.CHANNEL_IN_MONO,
79
- AudioFormat.ENCODING_PCM_16BIT,
80
- bufferSize * 2
81
- )
82
-
83
- if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) {
84
- promise.reject("AUDIO_ERROR", "AudioRecord initialization failed")
85
- return
86
- }
87
-
88
- // Configure audio processing
89
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
90
- val audioSessionId = audioRecord!!.audioSessionId
91
- // TODO: Attach AcousticEchoCanceler, NoiseSuppressor, AutomaticGainController
92
- }
93
-
94
- audioRecord!!.startRecording()
95
- captureRunning = true
96
-
97
- captureThread = Thread {
98
- val buffer = ShortArray(FRAME_SIZE)
99
- while (captureRunning) {
100
- val read = audioRecord?.read(buffer, 0, FRAME_SIZE) ?: -1
101
- if (read > 0) {
102
- // Send captured PCM samples to JavaScript
103
- val writableArray = Arguments.createArray()
104
- for (i in 0 until read) {
105
- writableArray.pushInt(buffer[i].toInt())
106
- }
107
- reactApplicationContext
108
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
109
- .emit(EVENT_AUDIO_CAPTURED, writableArray)
110
- }
111
- }
112
- }.apply {
113
- name = "CallumVoice-Capture"
114
- start()
115
- }
116
-
117
- promise.resolve(null)
118
- } catch (e: SecurityException) {
119
- promise.reject("PERMISSION_DENIED", "Microphone permission not granted", e)
120
- } catch (e: Exception) {
121
- promise.reject("AUDIO_ERROR", "Failed to start audio capture: ${e.message}", e)
122
- }
123
- }
124
-
125
- @ReactMethod
126
- fun stopAudioCapture(promise: Promise) {
127
- captureRunning = false
128
- try {
129
- captureThread?.join(1000)
130
- audioRecord?.stop()
131
- audioRecord?.release()
132
- } catch (e: Exception) {
133
- // ignore cleanup errors
134
- }
135
- audioRecord = null
136
- captureThread = null
137
- promise.resolve(null)
138
- }
139
-
140
- // ── Audio Playback ─────────────────────────────────────────────
141
-
142
- @ReactMethod
143
- fun startAudioPlayback(promise: Promise) {
144
- try {
145
- val bufferSize = AudioTrack.getMinBufferSize(
146
- SAMPLE_RATE,
147
- AudioFormat.CHANNEL_OUT_MONO,
148
- AudioFormat.ENCODING_PCM_16BIT
149
- )
150
-
151
- if (bufferSize == AudioTrack.ERROR || bufferSize == AudioTrack.ERROR_BAD_VALUE) {
152
- promise.reject("AUDIO_ERROR", "Cannot get minimum buffer size")
153
- return
154
- }
155
-
156
- val streamType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
157
- AudioManager.STREAM_VOICE_CALL
158
- } else {
159
- AudioManager.STREAM_VOICE_CALL
160
- }
161
-
162
- audioTrack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
163
- AudioTrack.Builder()
164
- .setAudioAttributes(
165
- android.media.AudioAttributes.Builder()
166
- .setUsage(android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION)
167
- .setContentType(android.media.AudioAttributes.CONTENT_TYPE_SPEECH)
168
- .build()
169
- )
170
- .setAudioFormat(
171
- AudioFormat.Builder()
172
- .setSampleRate(SAMPLE_RATE)
173
- .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
174
- .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
175
- .build()
176
- )
177
- .setBufferSizeInBytes(bufferSize * 2)
178
- .setTransferMode(AudioTrack.MODE_STREAM)
179
- .build()
180
- } else {
181
- @Suppress("DEPRECATION")
182
- AudioTrack(
183
- streamType,
184
- SAMPLE_RATE,
185
- AudioFormat.CHANNEL_OUT_MONO,
186
- AudioFormat.ENCODING_PCM_16BIT,
187
- bufferSize * 2,
188
- AudioTrack.MODE_STREAM
189
- )
190
- }
191
-
192
- if (audioTrack?.state != AudioTrack.STATE_INITIALIZED) {
193
- promise.reject("AUDIO_ERROR", "AudioTrack initialization failed")
194
- return
195
- }
196
-
197
- audioTrack!!.play()
198
- playbackRunning = true
199
-
200
- // TODO: Implement playback loop that receives audio from JS and writes to AudioTrack
201
- // For now, audio playback is handled via enqueuePeerAudio method
202
-
203
- promise.resolve(null)
204
- } catch (e: Exception) {
205
- promise.reject("AUDIO_ERROR", "Failed to start audio playback: ${e.message}", e)
206
- }
207
- }
208
-
209
- @ReactMethod
210
- fun stopAudioPlayback(promise: Promise) {
211
- playbackRunning = false
212
- try {
213
- playbackThread?.join(1000)
214
- audioTrack?.stop()
215
- audioTrack?.release()
216
- } catch (e: Exception) {
217
- // ignore cleanup errors
218
- }
219
- audioTrack = null
220
- playbackThread = null
221
- promise.resolve(null)
222
- }
223
-
224
- /**
225
- * Enqueue PCM audio samples for a specific peer.
226
- * Called from JavaScript when binary audio is received from the server.
227
- */
228
- @ReactMethod
229
- fun enqueuePeerAudio(peerId: String, samples: ReadableArray) {
230
- if (audioTrack == null || !playbackRunning) return
231
-
232
- val shortArray = ShortArray(samples.size())
233
- for (i in 0 until samples.size()) {
234
- shortArray[i] = samples.getInt(i).toShort()
235
- }
236
-
237
- audioTrack?.write(shortArray, 0, shortArray.size)
238
- }
239
-
240
- // ── Speaker Routing ────────────────────────────────────────────
241
-
242
- @ReactMethod
243
- fun setSpeakerphoneOn(on: Boolean, promise: Promise) {
244
- try {
245
- val audioManager = reactApplicationContext
246
- .getSystemService(ReactApplicationContext.AUDIO_SERVICE) as AudioManager
247
-
248
- audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
249
- audioManager.isSpeakerphoneOn = on
250
-
251
- promise.resolve(null)
252
- } catch (e: Exception) {
253
- promise.reject("AUDIO_ERROR", "Failed to set speakerphone: ${e.message}", e)
254
- }
255
- }
256
-
257
- // ── Audio Session ──────────────────────────────────────────────
258
-
259
- @ReactMethod
260
- fun configureAudioSession(promise: Promise) {
261
- try {
262
- val audioManager = reactApplicationContext
263
- .getSystemService(ReactApplicationContext.AUDIO_SERVICE) as AudioManager
264
-
265
- audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
266
- audioManager.isMusicActive = false
267
-
268
- promise.resolve(null)
269
- } catch (e: Exception) {
270
- promise.reject("AUDIO_ERROR", "Failed to configure audio session: ${e.message}", e)
271
- }
272
- }
273
-
274
- override fun onCatalystInstanceDestroy() {
275
- super.onCatalystInstanceDestroy()
276
- captureRunning = false
277
- playbackRunning = false
278
- try {
279
- audioRecord?.stop()
280
- audioRecord?.release()
281
- } catch (e: Exception) { }
282
- try {
283
- audioTrack?.stop()
284
- audioTrack?.release()
285
- } catch (e: Exception) { }
286
- audioRecord = null
287
- audioTrack = null
288
- }
289
- }
1
+ // Copyright Cloudfort. All Rights Reserved.
2
+
3
+ package ir.cloudfort.callum
4
+
5
+ import android.media.AudioFormat
6
+ import android.media.AudioManager
7
+ import android.media.AudioRecord
8
+ import android.media.AudioTrack
9
+ import android.media.MediaRecorder
10
+ import android.os.Build
11
+ import com.facebook.react.bridge.*
12
+ import com.facebook.react.modules.core.DeviceEventManagerModule
13
+
14
+ /**
15
+ * React Native native module for Android audio capture and playback.
16
+ *
17
+ * Handles microphone recording (AudioRecord) and audio playback (AudioTrack)
18
+ * using PCM 16-bit mono at 48000 Hz. Communicates audio data to/from the
19
+ * JavaScript layer via DeviceEventManagerModule events.
20
+ */
21
+ class CallumVoiceModule(reactContext: ReactApplicationContext)
22
+ : ReactContextBaseJavaModule(reactContext) {
23
+
24
+ companion object {
25
+ private const val MODULE_NAME = "CallumVoice"
26
+ private const val SAMPLE_RATE = 48000
27
+ private const val FRAME_SIZE = 960 // 20ms at 48kHz
28
+
29
+ // JS event names
30
+ private const val EVENT_AUDIO_CAPTURED = "onAudioCaptured"
31
+ private const val EVENT_AUDIO_ERROR = "onAudioError"
32
+ }
33
+
34
+ private var audioRecord: AudioRecord? = null
35
+ private var audioTrack: AudioTrack? = null
36
+ private var captureThread: Thread? = null
37
+ private var playbackThread: Thread? = null
38
+ private var captureRunning = false
39
+ private var playbackRunning = false
40
+
41
+ override fun getName(): String = MODULE_NAME
42
+
43
+ /**
44
+ * Exported constants accessible from JavaScript via NativeModules.CallumVoice.
45
+ */
46
+ override fun getConstants(): Map<String, Any> {
47
+ return hashMapOf(
48
+ "SAMPLE_RATE" to SAMPLE_RATE,
49
+ "FRAME_SIZE" to FRAME_SIZE
50
+ )
51
+ }
52
+
53
+ // ── Microphone Capture ─────────────────────────────────────────
54
+
55
+ @ReactMethod
56
+ fun startAudioCapture(promise: Promise) {
57
+ try {
58
+ val bufferSize = AudioRecord.getMinBufferSize(
59
+ SAMPLE_RATE,
60
+ AudioFormat.CHANNEL_IN_MONO,
61
+ AudioFormat.ENCODING_PCM_16BIT
62
+ )
63
+
64
+ if (bufferSize == AudioRecord.ERROR || bufferSize == AudioRecord.ERROR_BAD_VALUE) {
65
+ promise.reject("AUDIO_ERROR", "Cannot get minimum buffer size")
66
+ return
67
+ }
68
+
69
+ val audioSource = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
70
+ MediaRecorder.AudioSource.VOICE_COMMUNICATION
71
+ } else {
72
+ MediaRecorder.AudioSource.VOICE_COMMUNICATION
73
+ }
74
+
75
+ audioRecord = AudioRecord(
76
+ audioSource,
77
+ SAMPLE_RATE,
78
+ AudioFormat.CHANNEL_IN_MONO,
79
+ AudioFormat.ENCODING_PCM_16BIT,
80
+ bufferSize * 2
81
+ )
82
+
83
+ if (audioRecord?.state != AudioRecord.STATE_INITIALIZED) {
84
+ promise.reject("AUDIO_ERROR", "AudioRecord initialization failed")
85
+ return
86
+ }
87
+
88
+ // Configure audio processing
89
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
90
+ val audioSessionId = audioRecord!!.audioSessionId
91
+ // TODO: Attach AcousticEchoCanceler, NoiseSuppressor, AutomaticGainController
92
+ }
93
+
94
+ audioRecord!!.startRecording()
95
+ captureRunning = true
96
+
97
+ captureThread = Thread {
98
+ val buffer = ShortArray(FRAME_SIZE)
99
+ while (captureRunning) {
100
+ val read = audioRecord?.read(buffer, 0, FRAME_SIZE) ?: -1
101
+ if (read > 0) {
102
+ // Send captured PCM samples to JavaScript
103
+ val writableArray = Arguments.createArray()
104
+ for (i in 0 until read) {
105
+ writableArray.pushInt(buffer[i].toInt())
106
+ }
107
+ reactApplicationContext
108
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
109
+ .emit(EVENT_AUDIO_CAPTURED, writableArray)
110
+ }
111
+ }
112
+ }.apply {
113
+ name = "CallumVoice-Capture"
114
+ start()
115
+ }
116
+
117
+ promise.resolve(null)
118
+ } catch (e: SecurityException) {
119
+ promise.reject("PERMISSION_DENIED", "Microphone permission not granted", e)
120
+ } catch (e: Exception) {
121
+ promise.reject("AUDIO_ERROR", "Failed to start audio capture: ${e.message}", e)
122
+ }
123
+ }
124
+
125
+ @ReactMethod
126
+ fun stopAudioCapture(promise: Promise) {
127
+ captureRunning = false
128
+ try {
129
+ captureThread?.join(1000)
130
+ audioRecord?.stop()
131
+ audioRecord?.release()
132
+ } catch (e: Exception) {
133
+ // ignore cleanup errors
134
+ }
135
+ audioRecord = null
136
+ captureThread = null
137
+ promise.resolve(null)
138
+ }
139
+
140
+ // ── Audio Playback ─────────────────────────────────────────────
141
+
142
+ @ReactMethod
143
+ fun startAudioPlayback(promise: Promise) {
144
+ try {
145
+ val bufferSize = AudioTrack.getMinBufferSize(
146
+ SAMPLE_RATE,
147
+ AudioFormat.CHANNEL_OUT_MONO,
148
+ AudioFormat.ENCODING_PCM_16BIT
149
+ )
150
+
151
+ if (bufferSize == AudioTrack.ERROR || bufferSize == AudioTrack.ERROR_BAD_VALUE) {
152
+ promise.reject("AUDIO_ERROR", "Cannot get minimum buffer size")
153
+ return
154
+ }
155
+
156
+ val streamType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
157
+ AudioManager.STREAM_VOICE_CALL
158
+ } else {
159
+ AudioManager.STREAM_VOICE_CALL
160
+ }
161
+
162
+ audioTrack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
163
+ AudioTrack.Builder()
164
+ .setAudioAttributes(
165
+ android.media.AudioAttributes.Builder()
166
+ .setUsage(android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION)
167
+ .setContentType(android.media.AudioAttributes.CONTENT_TYPE_SPEECH)
168
+ .build()
169
+ )
170
+ .setAudioFormat(
171
+ AudioFormat.Builder()
172
+ .setSampleRate(SAMPLE_RATE)
173
+ .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
174
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
175
+ .build()
176
+ )
177
+ .setBufferSizeInBytes(bufferSize * 2)
178
+ .setTransferMode(AudioTrack.MODE_STREAM)
179
+ .build()
180
+ } else {
181
+ @Suppress("DEPRECATION")
182
+ AudioTrack(
183
+ streamType,
184
+ SAMPLE_RATE,
185
+ AudioFormat.CHANNEL_OUT_MONO,
186
+ AudioFormat.ENCODING_PCM_16BIT,
187
+ bufferSize * 2,
188
+ AudioTrack.MODE_STREAM
189
+ )
190
+ }
191
+
192
+ if (audioTrack?.state != AudioTrack.STATE_INITIALIZED) {
193
+ promise.reject("AUDIO_ERROR", "AudioTrack initialization failed")
194
+ return
195
+ }
196
+
197
+ audioTrack!!.play()
198
+ playbackRunning = true
199
+
200
+ // TODO: Implement playback loop that receives audio from JS and writes to AudioTrack
201
+ // For now, audio playback is handled via enqueuePeerAudio method
202
+
203
+ promise.resolve(null)
204
+ } catch (e: Exception) {
205
+ promise.reject("AUDIO_ERROR", "Failed to start audio playback: ${e.message}", e)
206
+ }
207
+ }
208
+
209
+ @ReactMethod
210
+ fun stopAudioPlayback(promise: Promise) {
211
+ playbackRunning = false
212
+ try {
213
+ playbackThread?.join(1000)
214
+ audioTrack?.stop()
215
+ audioTrack?.release()
216
+ } catch (e: Exception) {
217
+ // ignore cleanup errors
218
+ }
219
+ audioTrack = null
220
+ playbackThread = null
221
+ promise.resolve(null)
222
+ }
223
+
224
+ /**
225
+ * Enqueue PCM audio samples for a specific peer.
226
+ * Called from JavaScript when binary audio is received from the server.
227
+ */
228
+ @ReactMethod
229
+ fun enqueuePeerAudio(peerId: String, samples: ReadableArray) {
230
+ if (audioTrack == null || !playbackRunning) return
231
+
232
+ val shortArray = ShortArray(samples.size())
233
+ for (i in 0 until samples.size()) {
234
+ shortArray[i] = samples.getInt(i).toShort()
235
+ }
236
+
237
+ audioTrack?.write(shortArray, 0, shortArray.size)
238
+ }
239
+
240
+ // ── Speaker Routing ────────────────────────────────────────────
241
+
242
+ @ReactMethod
243
+ fun setSpeakerphoneOn(on: Boolean, promise: Promise) {
244
+ try {
245
+ val audioManager = reactApplicationContext
246
+ .getSystemService(ReactApplicationContext.AUDIO_SERVICE) as AudioManager
247
+
248
+ audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
249
+ audioManager.isSpeakerphoneOn = on
250
+
251
+ promise.resolve(null)
252
+ } catch (e: Exception) {
253
+ promise.reject("AUDIO_ERROR", "Failed to set speakerphone: ${e.message}", e)
254
+ }
255
+ }
256
+
257
+ // ── Audio Session ──────────────────────────────────────────────
258
+
259
+ @ReactMethod
260
+ fun configureAudioSession(promise: Promise) {
261
+ try {
262
+ val audioManager = reactApplicationContext
263
+ .getSystemService(ReactApplicationContext.AUDIO_SERVICE) as AudioManager
264
+
265
+ audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
266
+ audioManager.isMusicActive = false
267
+
268
+ promise.resolve(null)
269
+ } catch (e: Exception) {
270
+ promise.reject("AUDIO_ERROR", "Failed to configure audio session: ${e.message}", e)
271
+ }
272
+ }
273
+
274
+ override fun onCatalystInstanceDestroy() {
275
+ super.onCatalystInstanceDestroy()
276
+ captureRunning = false
277
+ playbackRunning = false
278
+ try {
279
+ audioRecord?.stop()
280
+ audioRecord?.release()
281
+ } catch (e: Exception) { }
282
+ try {
283
+ audioTrack?.stop()
284
+ audioTrack?.release()
285
+ } catch (e: Exception) { }
286
+ audioRecord = null
287
+ audioTrack = null
288
+ }
289
+ }
@@ -1,32 +1,32 @@
1
- // Copyright Cloudfort. All Rights Reserved.
2
-
3
- package com.callumvoice
4
-
5
- import com.facebook.react.ReactPackage
6
- import com.facebook.react.bridge.NativeModule
7
- import com.facebook.react.bridge.ReactApplicationContext
8
- import com.facebook.react.uimanager.ViewManager
9
-
10
- /**
11
- * React Native package registration for CallumVoice native module.
12
- *
13
- * Add this to your MainApplication.java/kt:
14
- * ```
15
- * override fun getPackages(): List<ReactPackage> {
16
- * return listOf(
17
- * MainReactPackage(),
18
- * CallumVoicePackage()
19
- * )
20
- * }
21
- * ```
22
- */
23
- class CallumVoicePackage : ReactPackage {
24
-
25
- override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
26
- return listOf(CallumVoiceModule(reactContext))
27
- }
28
-
29
- override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
30
- return emptyList()
31
- }
32
- }
1
+ // Copyright Cloudfort. All Rights Reserved.
2
+
3
+ package ir.cloudfort.callum
4
+
5
+ import com.facebook.react.ReactPackage
6
+ import com.facebook.react.bridge.NativeModule
7
+ import com.facebook.react.bridge.ReactApplicationContext
8
+ import com.facebook.react.uimanager.ViewManager
9
+
10
+ /**
11
+ * React Native package registration for CallumVoice native module.
12
+ *
13
+ * Add this to your MainApplication.java/kt:
14
+ * ```
15
+ * override fun getPackages(): List<ReactPackage> {
16
+ * return listOf(
17
+ * MainReactPackage(),
18
+ * CallumVoicePackage()
19
+ * )
20
+ * }
21
+ * ```
22
+ */
23
+ class CallumVoicePackage : ReactPackage {
24
+
25
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
26
+ return listOf(CallumVoiceModule(reactContext))
27
+ }
28
+
29
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
30
+ return emptyList()
31
+ }
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudfort/callum-voice",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Real-time voice chat SDK for React Native apps",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -46,3 +46,4 @@
46
46
  "source": "src/index.ts"
47
47
  }
48
48
 
49
+