diff --git a/node_modules/react-native-audio-recorder-player/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties b/node_modules/react-native-audio-recorder-player/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties new file mode 100644 index 0000000..1211b1e --- /dev/null +++ b/node_modules/react-native-audio-recorder-player/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties @@ -0,0 +1,6 @@ +aarFormatVersion=1.0 +aarMetadataVersion=1.0 +minCompileSdk=1 +minCompileSdkExtension=0 +minAndroidGradlePluginVersion=1.0.0 +coreLibraryDesugaringEnabled=false diff --git a/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt b/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt index 5cb9bbf..25090aa 100644 --- a/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt +++ b/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt @@ -18,7 +18,12 @@ import java.io.IOException import java.util.* import kotlin.math.log10 -class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), PermissionListener { +class RNAudioRecorderPlayerModule( + private val reactApplicationContext: ReactApplicationContext +) : ReactContextBaseJavaModule(reactApplicationContext), + PermissionListener, + LifecycleEventListener { + private var audioFileURL = "" private var subsDurationMillis = 500 private var _meteringEnabled = false @@ -29,345 +34,206 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont private var mTimer: Timer? = null private var pausedRecordTime = 0L private var totalPausedRecordTime = 0L - var recordHandler: Handler? = Handler(Looper.getMainLooper()) - override fun getName(): String { - return tag - } + private var recordHandler: Handler? = Handler(Looper.getMainLooper()) + + private val activity + get() = reactApplicationContext.currentActivity + + private val appContext + get() = reactApplicationContext.applicationContext + + override fun getName(): String = tag @ReactMethod - fun startRecorder(path: String, audioSet: ReadableMap?, meteringEnabled: Boolean, promise: Promise) { + fun startRecorder( + path: String, + audioSet: ReadableMap?, + meteringEnabled: Boolean, + promise: Promise + ) { + if (activity == null) { + promise.reject("NO_ACTIVITY", "Activity not attached") + return + } + try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // TIRAMISU (33) - // https://github.com/hyochan/react-native-audio-recorder-player/issues/503 - if (Build.VERSION.SDK_INT < 33 && - (ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED || - ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) { - ActivityCompat.requestPermissions((currentActivity)!!, arrayOf( - Manifest.permission.RECORD_AUDIO, - Manifest.permission.WRITE_EXTERNAL_STORAGE), 0) - promise.reject("No permission granted.", "Try again after adding permission.") - return - } else if (ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions((currentActivity)!!, arrayOf(Manifest.permission.RECORD_AUDIO), 0) - promise.reject("No permission granted.", "Try again after adding permission.") + if (ActivityCompat.checkSelfPermission( + appContext, + Manifest.permission.RECORD_AUDIO + ) != PackageManager.PERMISSION_GRANTED + ) { + ActivityCompat.requestPermissions( + activity!!, + arrayOf(Manifest.permission.RECORD_AUDIO), + 0 + ) + promise.reject("PERMISSION_DENIED", "RECORD_AUDIO permission not granted") return } } - } catch (ne: NullPointerException) { - Log.w(tag, ne.toString()) - promise.reject("No permission granted.", "Try again after adding permission.") + } catch (e: Exception) { + promise.reject("PERMISSION_ERROR", e.message) return } - audioFileURL = if (((path == "DEFAULT"))) "${reactContext.cacheDir}/$defaultFileName" else path + + val outputFormat = + if (audioSet?.hasKey("OutputFormatAndroid") == true) + audioSet.getInt("OutputFormatAndroid") + else + MediaRecorder.OutputFormat.MPEG_4 + + audioFileURL = + if (path == "DEFAULT") + "${reactApplicationContext.cacheDir}/sound.${defaultFileExtensions[outputFormat]}" + else path + _meteringEnabled = meteringEnabled - if (mediaRecorder == null) { - mediaRecorder = MediaRecorder() + if (mediaRecorder != null) { + promise.reject("InvalidState", "Recorder already running") + return } - if (audioSet != null) { - mediaRecorder!!.setAudioSource(if (audioSet.hasKey("AudioSourceAndroid")) audioSet.getInt("AudioSourceAndroid") else MediaRecorder.AudioSource.MIC) - mediaRecorder!!.setOutputFormat(if (audioSet.hasKey("OutputFormatAndroid")) audioSet.getInt("OutputFormatAndroid") else MediaRecorder.OutputFormat.MPEG_4) - mediaRecorder!!.setAudioEncoder(if (audioSet.hasKey("AudioEncoderAndroid")) audioSet.getInt("AudioEncoderAndroid") else MediaRecorder.AudioEncoder.AAC) - mediaRecorder!!.setAudioSamplingRate(if (audioSet.hasKey("AudioSamplingRateAndroid")) audioSet.getInt("AudioSamplingRateAndroid") else 48000) - mediaRecorder!!.setAudioEncodingBitRate(if (audioSet.hasKey("AudioEncodingBitRateAndroid")) audioSet.getInt("AudioEncodingBitRateAndroid") else 128000) - } else { - mediaRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC) - mediaRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) - mediaRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) - mediaRecorder!!.setAudioEncodingBitRate(128000) - mediaRecorder!!.setAudioSamplingRate(48000) - } - mediaRecorder!!.setOutputFile(audioFileURL) + val recorder = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + MediaRecorder(appContext) + else MediaRecorder() try { - mediaRecorder!!.prepare() + recorder.setAudioSource(MediaRecorder.AudioSource.MIC) + recorder.setOutputFormat(outputFormat) + recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) + recorder.setOutputFile(audioFileURL) + + recorder.prepare() + recorder.start() + + mediaRecorder = recorder totalPausedRecordTime = 0L - mediaRecorder!!.start() - val systemTime = SystemClock.elapsedRealtime() + + val startTime = SystemClock.elapsedRealtime() recorderRunnable = object : Runnable { override fun run() { - val time = SystemClock.elapsedRealtime() - systemTime - totalPausedRecordTime - val obj = Arguments.createMap() - obj.putDouble("currentPosition", time.toDouble()) + val time = + SystemClock.elapsedRealtime() - startTime - totalPausedRecordTime + val map = Arguments.createMap() + map.putDouble("currentPosition", time.toDouble()) + if (_meteringEnabled) { - var maxAmplitude = 0 - if (mediaRecorder != null) { - maxAmplitude = mediaRecorder!!.maxAmplitude - } - var dB = -160.0 - val maxAudioSize = 32767.0 - if (maxAmplitude > 0) { - dB = 20 * log10(maxAmplitude / maxAudioSize) - } - obj.putInt("currentMetering", dB.toInt()) + val amp = mediaRecorder?.maxAmplitude ?: 0 + val dB = + if (amp > 0) (20 * log10(amp / 32767.0)).toInt() else -160 + map.putInt("currentMetering", dB) } - sendEvent(reactContext, "rn-recordback", obj) - recordHandler!!.postDelayed(this, subsDurationMillis.toLong()) + + sendEvent("rn-recordback", map) + recordHandler?.postDelayed(this, subsDurationMillis.toLong()) } } - (recorderRunnable as Runnable).run() - promise.resolve("file:///$audioFileURL") - } catch (e: Exception) { - Log.e(tag, "Exception: ", e) - promise.reject("startRecord", e.message) - } - } - @ReactMethod - fun resumeRecorder(promise: Promise) { - if (mediaRecorder == null) { - promise.reject("resumeReocrder", "Recorder is null.") - return - } - - try { - mediaRecorder!!.resume() - totalPausedRecordTime += SystemClock.elapsedRealtime() - pausedRecordTime; - recorderRunnable?.let { recordHandler!!.postDelayed(it, subsDurationMillis.toLong()) } - promise.resolve("Recorder resumed.") - } catch (e: Exception) { - Log.e(tag, "Recorder resume: " + e.message) - promise.reject("resumeRecorder", e.message) - } - } - - @ReactMethod - fun pauseRecorder(promise: Promise) { - if (mediaRecorder == null) { - promise.reject("pauseRecorder", "Recorder is null.") - return - } + recorderRunnable?.run() + promise.resolve("file:///$audioFileURL") - try { - mediaRecorder!!.pause() - pausedRecordTime = SystemClock.elapsedRealtime(); - recorderRunnable?.let { recordHandler!!.removeCallbacks(it) }; - promise.resolve("Recorder paused.") } catch (e: Exception) { - Log.e(tag, "pauseRecorder exception: " + e.message) - promise.reject("pauseRecorder", e.message) + recorder.release() + mediaRecorder = null + promise.reject("startRecorder", e.message) } } @ReactMethod fun stopRecorder(promise: Promise) { - if (recordHandler != null) { - recorderRunnable?.let { recordHandler!!.removeCallbacks(it) } - } - - if (mediaRecorder == null) { - promise.reject("stopRecord", "recorder is null.") - return - } - try { - mediaRecorder!!.stop() - } catch (stopException: RuntimeException) { - stopException.message?.let { Log.d(tag,"" + it) } - promise.reject("stopRecord", stopException.message) - } - - mediaRecorder!!.release() - mediaRecorder = null - promise.resolve("file:///$audioFileURL") - } - - @ReactMethod - fun setVolume(volume: Double, promise: Promise) { - if (mediaPlayer == null) { - promise.reject("setVolume", "player is null.") - return + recordHandler?.removeCallbacks(recorderRunnable!!) + mediaRecorder?.stop() + mediaRecorder?.release() + mediaRecorder = null + promise.resolve("file:///$audioFileURL") + } catch (e: Exception) { + promise.reject("stopRecorder", e.message) } - - val mVolume = volume.toFloat() - mediaPlayer!!.setVolume(mVolume, mVolume) - promise.resolve("set volume") } @ReactMethod - fun startPlayer(path: String, httpHeaders: ReadableMap?, promise: Promise) { + fun startPlayer(path: String, headers: ReadableMap?, promise: Promise) { if (mediaPlayer != null) { - val isPaused = !mediaPlayer!!.isPlaying && mediaPlayer!!.currentPosition > 1 - - if (isPaused) { - mediaPlayer!!.start() - promise.resolve("player resumed.") - return - } - - Log.e(tag, "Player is already running. Stop it first.") - promise.reject("startPlay", "Player is already running. Stop it first.") + promise.reject("PLAYER_RUNNING", "Player already running") return - } else { - mediaPlayer = MediaPlayer() } + mediaPlayer = MediaPlayer() + try { - if ((path == "DEFAULT")) { - mediaPlayer!!.setDataSource("${reactContext.cacheDir}/$defaultFileName") - } else { - if (httpHeaders != null) { - val headers: MutableMap = HashMap() - val iterator = httpHeaders.keySetIterator() - while (iterator.hasNextKey()) { - val key = iterator.nextKey() - headers.put(key, httpHeaders.getString(key)) - } - mediaPlayer!!.setDataSource(currentActivity!!.applicationContext, Uri.parse(path), headers) - } else { - mediaPlayer!!.setDataSource(path) + if (headers != null) { + val map = HashMap() + val it = headers.keySetIterator() + while (it.hasNextKey()) { + val k = it.nextKey() + map[k] = headers.getString(k) } + mediaPlayer!!.setDataSource(appContext, Uri.parse(path), map) + } else { + mediaPlayer!!.setDataSource(path) } - mediaPlayer!!.setOnPreparedListener { mp -> - Log.d(tag, "mediaplayer prepared and start") - mp.start() - /** - * Set timer task to send event to RN. - */ - mTask = object : TimerTask() { - override fun run() { - val obj = Arguments.createMap() - obj.putInt("duration", mp.duration) - obj.putInt("currentPosition", mp.currentPosition) - sendEvent(reactContext, "rn-playback", obj) - } - } - - mTimer = Timer() - mTimer!!.schedule(mTask, 0, subsDurationMillis.toLong()) - val resolvedPath = if (((path == "DEFAULT"))) "${reactContext.cacheDir}/$defaultFileName" else path - promise.resolve(resolvedPath) + mediaPlayer!!.setOnPreparedListener { + it.start() + promise.resolve(path) } - /** - * Detect when finish playing. - */ - mediaPlayer!!.setOnCompletionListener { mp -> - /** - * Send last event - */ - val obj = Arguments.createMap() - obj.putInt("duration", mp.duration) - obj.putInt("currentPosition", mp.duration) - sendEvent(reactContext, "rn-playback", obj) - /** - * Reset player. - */ - Log.d(tag, "Plays completed.") - mTimer!!.cancel() - mp.stop() - mp.release() + mediaPlayer!!.setOnCompletionListener { + sendEvent("rn-playback", Arguments.createMap().apply { + putBoolean("isFinished", true) + }) + it.release() mediaPlayer = null } - mediaPlayer!!.prepare() + mediaPlayer!!.prepareAsync() } catch (e: IOException) { - Log.e(tag, "startPlay() io exception") - promise.reject("startPlay", e.message) - } catch (e: NullPointerException) { - Log.e(tag, "startPlay() null exception") + promise.reject("startPlayer", e.message) } } - @ReactMethod - fun resumePlayer(promise: Promise) { - if (mediaPlayer == null) { - promise.reject("resume", "mediaPlayer is null on resume.") - return - } - - if (mediaPlayer!!.isPlaying) { - promise.reject("resume", "mediaPlayer is already running.") - return - } - - try { - mediaPlayer!!.seekTo(mediaPlayer!!.currentPosition) - mediaPlayer!!.start() - promise.resolve("resume player") - } catch (e: Exception) { - Log.e(tag, "mediaPlayer resume: " + e.message) - promise.reject("resume", e.message) - } + private fun sendEvent(event: String, params: WritableMap?) { + reactApplicationContext + .getJSModule(RCTDeviceEventEmitter::class.java) + .emit(event, params) } - @ReactMethod - fun pausePlayer(promise: Promise) { - if (mediaPlayer == null) { - promise.reject("pausePlay", "mediaPlayer is null on pause.") - return - } - + override fun onHostDestroy() { try { - mediaPlayer!!.pause() - promise.resolve("pause player") - } catch (e: Exception) { - Log.e(tag, "pausePlay exception: " + e.message) - promise.reject("pausePlay", e.message) + mediaRecorder?.release() + mediaPlayer?.release() + } catch (_: Exception) { } } - @ReactMethod - fun seekToPlayer(time: Double, promise: Promise) { - if (mediaPlayer == null) { - promise.reject("seekTo", "mediaPlayer is null on seek.") - return - } - - mediaPlayer!!.seekTo(time.toInt()) - promise.resolve("pause player") - } - - private fun sendEvent(reactContext: ReactContext, - eventName: String, - params: WritableMap?) { - reactContext - .getJSModule(RCTDeviceEventEmitter::class.java) - .emit(eventName, params) - } - - @ReactMethod - fun stopPlayer(promise: Promise) { - if (mTimer != null) { - mTimer!!.cancel() - } - - if (mediaPlayer == null) { - promise.resolve("Already stopped player") - return - } + override fun onHostPause() {} + override fun onHostResume() {} - try { - mediaPlayer!!.release() - mediaPlayer = null - promise.resolve("stopped player") - } catch (e: Exception) { - Log.e(tag, "stopPlay exception: " + e.message) - promise.reject("stopPlay", e.message) - } - } - - @ReactMethod - fun setSubscriptionDuration(sec: Double, promise: Promise) { - subsDurationMillis = (sec * 1000).toInt() - promise.resolve("setSubscriptionDuration: $subsDurationMillis") - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray): Boolean { - var requestRecordAudioPermission: Int = 200 - - when (requestCode) { - requestRecordAudioPermission -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) return true - } - - return false - } + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ): Boolean = true companion object { - private var tag = "RNAudioRecorderPlayer" - private var defaultFileName = "sound.mp4" + private const val tag = "RNAudioRecorderPlayer" + private val defaultFileExtensions = listOf( + "mp4", + "3gp", + "mp4", + "amr", + "amr", + "aac", + "aac", + "rtp", + "ts", + "webm", + "xxx", + "ogg" + ) } }