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 4570fc2..8db4b61 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,7 @@ import java.io.IOException import java.util.* import kotlin.math.log10 -class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), PermissionListener, LifecycleEventListener { +class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), PermissionListener { private var audioFileURL = "" private var subsDurationMillis = 500 private var _meteringEnabled = false @@ -38,78 +38,72 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont fun startRecorder(path: String, audioSet: ReadableMap?, meteringEnabled: Boolean, promise: Promise) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // On devices that run Android 10 (API level 29) or higher - // your app can contribute to well-defined media collections such as MediaStore.Downloads without requesting any storage-related permissions - // https://developer.android.com/about/versions/11/privacy/storage#permissions-target-11 - if (Build.VERSION.SDK_INT < 29 && - (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.") - return - } - } + val activity = reactContext.currentActivity + + // Agar activity null hai (rare case), to direct reject kar do + if (activity == null) { + promise.reject("No permission granted.", "Activity is null.") + return + } + + 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( + activity, + 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( + activity, + arrayOf(Manifest.permission.RECORD_AUDIO), + 0 + ) + promise.reject("No permission granted.", "Try again after adding permission.") + return + } +} + } catch (ne: NullPointerException) { Log.w(tag, ne.toString()) promise.reject("No permission granted.", "Try again after adding permission.") return } - - var outputFormat = if (audioSet != null && audioSet.hasKey("OutputFormatAndroid")) - audioSet.getInt("OutputFormatAndroid") - else - MediaRecorder.OutputFormat.MPEG_4 - - audioFileURL = if (((path == "DEFAULT"))) "${reactContext.cacheDir}/sound.${defaultFileExtensions.get(outputFormat)}" else path + audioFileURL = if (((path == "DEFAULT"))) "${reactContext.cacheDir}/$defaultFileName" else path _meteringEnabled = meteringEnabled - if (mediaRecorder != null) { - promise.reject("InvalidState", "startRecorder has already been called.") - return + if (mediaRecorder == null) { + mediaRecorder = MediaRecorder() } - var newMediaRecorder: MediaRecorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - MediaRecorder(reactContext) + 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() + 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) try { - if (audioSet == null) { - newMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC) - newMediaRecorder.setOutputFormat(outputFormat) - newMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) - } else { - newMediaRecorder.setAudioSource(if (audioSet.hasKey("AudioSourceAndroid")) audioSet.getInt("AudioSourceAndroid") else MediaRecorder.AudioSource.MIC) - newMediaRecorder.setOutputFormat(outputFormat) - newMediaRecorder.setAudioEncoder(if (audioSet.hasKey("AudioEncoderAndroid")) audioSet.getInt("AudioEncoderAndroid") else MediaRecorder.AudioEncoder.AAC) - - if (audioSet.hasKey("AudioSamplingRateAndroid")) { - newMediaRecorder.setAudioSamplingRate(audioSet.getInt("AudioSamplingRateAndroid")) - } - - if (audioSet.hasKey("AudioEncodingBitRateAndroid")) { - newMediaRecorder.setAudioEncodingBitRate(audioSet.getInt("AudioEncodingBitRateAndroid")) - } - - if (audioSet.hasKey("AudioChannelsAndroid")) { - newMediaRecorder.setAudioChannels(audioSet.getInt("AudioChannelsAndroid")) - } - } - newMediaRecorder.setOutputFile(audioFileURL) - - newMediaRecorder.prepare() + mediaRecorder!!.prepare() totalPausedRecordTime = 0L - newMediaRecorder.start() - - mediaRecorder = newMediaRecorder - + mediaRecorder!!.start() val systemTime = SystemClock.elapsedRealtime() recorderRunnable = object : Runnable { override fun run() { @@ -135,9 +129,6 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont (recorderRunnable as Runnable).run() promise.resolve("file:///$audioFileURL") } catch (e: Exception) { - newMediaRecorder.release() - mediaRecorder = null - Log.e(tag, "Exception: ", e) promise.reject("startRecord", e.message) } @@ -146,7 +137,7 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont @ReactMethod fun resumeRecorder(promise: Promise) { if (mediaRecorder == null) { - promise.reject("resumeRecorder", "Recorder is null.") + promise.reject("resumeReocrder", "Recorder is null.") return } @@ -192,13 +183,14 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont try { mediaRecorder!!.stop() - mediaRecorder!!.release() - mediaRecorder = null - promise.resolve("file:///$audioFileURL") } catch (stopException: RuntimeException) { stopException.message?.let { Log.d(tag,"" + it) } promise.reject("stopRecord", stopException.message) } + + mediaRecorder!!.release() + mediaRecorder = null + promise.resolve("file:///$audioFileURL") } @ReactMethod @@ -213,16 +205,6 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont promise.resolve("set volume") } - @ReactMethod - fun setPlaybackSpeed(playbackSpeed: Float, promise: Promise) { - if (mediaPlayer == null) { - promise.reject("setPlaybackSpeed", "player is null.") - return - } - mediaPlayer!!.playbackParams = mediaPlayer!!.playbackParams.setSpeed(playbackSpeed) - promise.resolve("setPlaybackSpeed") - } - @ReactMethod fun startPlayer(path: String, httpHeaders: ReadableMap?, promise: Promise) { if (mediaPlayer != null) { @@ -245,37 +227,36 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont 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 (httpHeaders != null) { + val headers: MutableMap = HashMap() + val iterator = httpHeaders.keySetIterator() + while (iterator.hasNextKey()) { + val key = iterator.nextKey() + headers[key] = httpHeaders.getString(key) + } + + val activity = reactContext.currentActivity + val context = activity?.applicationContext ?: reactContext.applicationContext + + mediaPlayer!!.setDataSource(context, Uri.parse(path), headers) +} else { + mediaPlayer!!.setDataSource(path) +} + } mediaPlayer!!.setOnPreparedListener { mp -> - Log.d(tag, "Mediaplayer prepared and start") + Log.d(tag, "mediaplayer prepared and start") mp.start() /** * Set timer task to send event to RN. */ mTask = object : TimerTask() { override fun run() { - try { - val obj = Arguments.createMap() - obj.putInt("duration", mp.duration) - obj.putInt("currentPosition", mp.currentPosition) - obj.putBoolean("isFinished", false); - sendEvent(reactContext, "rn-playback", obj) - } catch (e: IllegalStateException) { - // IllegalStateException 처리 - Log.e(tag, "Mediaplayer error: ${e.message}") - } + val obj = Arguments.createMap() + obj.putInt("duration", mp.duration) + obj.putInt("currentPosition", mp.currentPosition) + sendEvent(reactContext, "rn-playback", obj) } } @@ -294,16 +275,14 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont */ val obj = Arguments.createMap() obj.putInt("duration", mp.duration) - obj.putInt("currentPosition", mp.currentPosition) - obj.putBoolean("isFinished", true); + obj.putInt("currentPosition", mp.duration) sendEvent(reactContext, "rn-playback", obj) /** * Reset player. */ Log.d(tag, "Plays completed.") - mTimer?.cancel() + mTimer!!.cancel() mp.stop() - mp.reset() mp.release() mediaPlayer = null } @@ -320,12 +299,12 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont @ReactMethod fun resumePlayer(promise: Promise) { if (mediaPlayer == null) { - promise.reject("resume", "Mediaplayer is null on resume.") + promise.reject("resume", "mediaPlayer is null on resume.") return } if (mediaPlayer!!.isPlaying) { - promise.reject("resume", "Mediaplayer is already running.") + promise.reject("resume", "mediaPlayer is already running.") return } @@ -334,7 +313,7 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont mediaPlayer!!.start() promise.resolve("resume player") } catch (e: Exception) { - Log.e(tag, "Mediaplayer resume: " + e.message) + Log.e(tag, "mediaPlayer resume: " + e.message) promise.reject("resume", e.message) } } @@ -342,7 +321,7 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont @ReactMethod fun pausePlayer(promise: Promise) { if (mediaPlayer == null) { - promise.reject("pausePlay", "Mediaplayer is null on pause.") + promise.reject("pausePlay", "mediaPlayer is null on pause.") return } @@ -358,7 +337,7 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont @ReactMethod fun seekToPlayer(time: Double, promise: Promise) { if (mediaPlayer == null) { - promise.reject("seekTo", "Mediaplayer is null on seek.") + promise.reject("seekTo", "mediaPlayer is null on seek.") return } @@ -386,8 +365,6 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont } try { - mediaPlayer!!.stop() - mediaPlayer!!.reset() mediaPlayer!!.release() mediaPlayer = null promise.resolve("stopped player") @@ -413,51 +390,8 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont return false } - init { - reactContext.addLifecycleEventListener(this) - } - - override fun onHostDestroy() { - autoSaveRecordingIfNeeded() - } - - override fun onHostPause() {} - - override fun onHostResume() {} - - private fun autoSaveRecordingIfNeeded() { - if (mediaRecorder != null) { - try { - mediaRecorder!!.stop() - mediaRecorder!!.release() - mediaRecorder = null - Log.d(tag, "Recording auto-saved on app pause/destroy.") - } catch (e: Exception) { - Log.e(tag, "Error auto-saving recording: ${e.message}") - } - } - } - - protected fun finalize() { - reactContext.removeLifecycleEventListener(this) - } - companion object { private var tag = "RNAudioRecorderPlayer" private var defaultFileName = "sound.mp4" - private var defaultFileExtensions = listOf( - "mp4", // DEFAULT = 0 - "3gp", // THREE_GPP - "mp4", // MPEG_4 - "amr", // AMR_NB - "amr", // AMR_WB - "aac", // AAC_ADIF - "aac", // AAC_ADTS - "rtp", // OUTPUT_FORMAT_RTP_AVP - "ts", // MPEG_2_TSMPEG_2_TS - "webm",// WEBM - "xxx", // UNUSED - "ogg", // OGG - ) } }