diff --git a/README.md b/README.md index 1ec7b3a..1aec77c 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ A screen record module for React Native. - Support Android - minSdkVersion = 26 - - compileSdkVersion = 34 - - targetSdkVersion = 34 + - compileSdkVersion = 36 + - targetSdkVersion = 36 - use [HBRecorder](https://github.com/HBiSoft/HBRecorder) ## Installation ```sh -npm install react-native-record-screen +npm install https://github.com/alexpap98/react-native-record-screen.git#fix/react-native-support-0.81.0 ``` ### iOS diff --git a/android/build.gradle b/android/build.gradle index 3e6c024..5fb15a3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath "com.android.tools.build:gradle:7.2.1" + classpath "com.android.tools.build:gradle:8.5.2" // noinspection DifferentKotlinGradleVersion classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } @@ -21,7 +21,6 @@ def isNewArchitectureEnabled() { apply plugin: "com.android.library" apply plugin: "kotlin-android" - def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } if (isNewArchitectureEnabled()) { @@ -46,37 +45,45 @@ def supportsNamespace() { } android { - if (supportsNamespace()) { - namespace "com.recordscreen" + compileSdk getExtOrIntegerDefault("compileSdkVersion") + + namespace "com.recordscreen" - sourceSets { - main { - manifest.srcFile "src/main/AndroidManifestNew.xml" - } + sourceSets { + main { + manifest.srcFile "src/main/AndroidManifestNew.xml" } } - compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") defaultConfig { - minSdkVersion getExtOrIntegerDefault("minSdkVersion") - targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") + minSdk getExtOrIntegerDefault("minSdkVersion") + targetSdk getExtOrIntegerDefault("targetSdkVersion") buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() } + buildTypes { release { minifyEnabled false } } - lintOptions { + lint { disable "GradleCompatible" } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" } + packagingOptions { + pickFirst '**/libc++_shared.so' + pickFirst '**/libjsc.so' + } } repositories { @@ -92,6 +99,9 @@ dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "androidx.appcompat:appcompat:1.7.0" + implementation "androidx.activity:activity-ktx:1.9.3" + implementation "androidx.fragment:fragment-ktx:1.8.5" implementation "com.github.HBiSoft:HBRecorder:3.0.3" } @@ -101,4 +111,4 @@ if (isNewArchitectureEnabled()) { libraryName = "RecordScreen" codegenJavaPackageName = "com.recordscreen" } -} +} \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index 90fc280..6783b65 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ -RecordScreen_kotlinVersion=1.7.0 -RecordScreen_minSdkVersion=21 -RecordScreen_targetSdkVersion=31 -RecordScreen_compileSdkVersion=31 -RecordScreen_ndkversion=21.4.7075529 +RecordScreen_kotlinVersion=2.1.20 +RecordScreen_minSdkVersion=23 +RecordScreen_targetSdkVersion=36 +RecordScreen_compileSdkVersion=36 +RecordScreen_ndkversion=27.1.12297006 \ No newline at end of file diff --git a/android/src/main/java/com/recordscreen/RecordScreenModule.kt b/android/src/main/java/com/recordscreen/RecordScreenModule.kt index e3850d5..8066b0d 100644 --- a/android/src/main/java/com/recordscreen/RecordScreenModule.kt +++ b/android/src/main/java/com/recordscreen/RecordScreenModule.kt @@ -4,39 +4,52 @@ import android.app.Activity import android.app.Application import android.content.Context import android.content.Intent +import android.content.ContentValues import android.media.MediaCodecList import android.media.projection.MediaProjectionManager +import android.net.Uri import android.os.Build +import android.os.Environment +import android.provider.MediaStore import android.util.SparseIntArray import android.view.Surface +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import com.facebook.react.bridge.* +import com.facebook.react.modules.core.DeviceEventManagerModule import com.hbisoft.hbrecorder.HBRecorder import com.hbisoft.hbrecorder.HBRecorderListener import java.io.File +import java.io.FileInputStream import java.io.IOException +import java.io.OutputStream +import java.text.SimpleDateFormat +import java.util.* import kotlin.math.ceil class RecordScreenModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), HBRecorderListener { - private var hbRecorder: HBRecorder? = null; - private var screenWidth: Number = 0; - private var screenHeight: Number = 0; - private var mic: Boolean = true; - private var currentVersion: String = ""; - private var outputUri: File? = null; - private var startPromise: Promise? = null; - private var stopPromise: Promise? = null; + private var hbRecorder: HBRecorder? = null + private var screenWidth: Number = 0 + private var screenHeight: Number = 0 + private var mic: Boolean = true + private var currentVersion: String = "" + private var outputUri: File? = null + private var startPromise: Promise? = null + private var stopPromise: Promise? = null + private var activityResultLauncher: ActivityResultLauncher? = null companion object { - private val ORIENTATIONS = SparseIntArray(); - const val SCREEN_RECORD_REQUEST_CODE = 1000; + private val ORIENTATIONS = SparseIntArray() + const val SCREEN_RECORD_REQUEST_CODE = 1000 init { - ORIENTATIONS.append(Surface.ROTATION_0, 90); - ORIENTATIONS.append(Surface.ROTATION_90, 0); - ORIENTATIONS.append(Surface.ROTATION_180, 270); - ORIENTATIONS.append(Surface.ROTATION_270, 180); + ORIENTATIONS.append(Surface.ROTATION_0, 90) + ORIENTATIONS.append(Surface.ROTATION_90, 0) + ORIENTATIONS.append(Surface.ROTATION_180, 270) + ORIENTATIONS.append(Surface.ROTATION_270, 180) } } @@ -47,138 +60,342 @@ class RecordScreenModule(reactContext: ReactApplicationContext) : ReactContextBa private val mActivityEventListener: ActivityEventListener = object : BaseActivityEventListener() { override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, intent: Intent?) { if (requestCode == SCREEN_RECORD_REQUEST_CODE) { - if (resultCode == AppCompatActivity.RESULT_OK) { - hbRecorder!!.startScreenRecording(intent, resultCode); + if (resultCode == AppCompatActivity.RESULT_OK && intent != null) { + hbRecorder?.startScreenRecording(intent, resultCode) + startPromise?.resolve("started") } else { - startPromise!!.resolve("permission_error"); + startPromise?.resolve("permission_error") } + startPromise = null } else { - startPromise!!.reject("404", "cancel!"); + startPromise?.reject("404", "cancel!") + startPromise = null } - startPromise!!.resolve("started"); } } override fun initialize() { super.initialize() currentVersion = Build.VERSION.SDK_INT.toString() - outputUri = reactApplicationContext.getExternalFilesDir("ReactNativeRecordScreen"); + + try { + outputUri = reactApplicationContext.getExternalFilesDir("ReactNativeRecordScreen") + if (outputUri == null) { + outputUri = File(reactApplicationContext.filesDir, "ReactNativeRecordScreen") + if (!outputUri!!.exists()) { + outputUri!!.mkdirs() + } + } + } catch (e: Exception) { + e.printStackTrace() + } + + // Initialize activity result launcher for newer React Native versions + UiThreadUtil.runOnUiThread { + val currentActivity = reactApplicationContext.currentActivity + if (currentActivity is AppCompatActivity) { + try { + activityResultLauncher = currentActivity.registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) { + hbRecorder?.startScreenRecording(result.data, result.resultCode) + startPromise?.resolve("started") + } else { + startPromise?.resolve("permission_error") + } + startPromise = null + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + reactApplicationContext.addActivityEventListener(mActivityEventListener) + } + + override fun onCatalystInstanceDestroy() { + super.onCatalystInstanceDestroy() + reactApplicationContext.removeActivityEventListener(mActivityEventListener) + hbRecorder = null } @ReactMethod fun setup(readableMap: ReadableMap) { - Application().onCreate() - screenWidth = if (readableMap.hasKey("width")) ceil(readableMap.getDouble("width")).toInt() else 0; - screenHeight = if (readableMap.hasKey("height")) ceil(readableMap.getDouble("height")).toInt() else 0; - mic = if (readableMap.hasKey("mic")) readableMap.getBoolean("mic") else true; + try { + screenWidth = if (readableMap.hasKey("width")) ceil(readableMap.getDouble("width")).toInt() else 0 + screenHeight = if (readableMap.hasKey("height")) ceil(readableMap.getDouble("height")).toInt() else 0 + mic = if (readableMap.hasKey("mic")) readableMap.getBoolean("mic") else true - hbRecorder = HBRecorder(reactApplicationContext, this); - hbRecorder!!.setOutputPath(outputUri.toString()); + hbRecorder = HBRecorder(reactApplicationContext, this) + + outputUri?.let { uri -> + hbRecorder?.setOutputPath(uri.toString()) + } - // For FPS and bitrate we need to enable custom settings - if (readableMap.hasKey("fps") || readableMap.hasKey("bitrate")) { - hbRecorder!!.enableCustomSettings(); + // For FPS and bitrate we need to enable custom settings + if (readableMap.hasKey("fps") || readableMap.hasKey("bitrate")) { + hbRecorder?.enableCustomSettings() - if (readableMap.hasKey("fps")) { - val fps = readableMap.getInt("fps"); - hbRecorder!!.setVideoFrameRate(fps); - } - if (readableMap.hasKey("bitrate")) { - val bitrate = readableMap.getInt("bitrate"); - hbRecorder!!.setVideoBitrate(bitrate); + if (readableMap.hasKey("fps")) { + val fps = readableMap.getInt("fps") + hbRecorder?.setVideoFrameRate(fps) + } + if (readableMap.hasKey("bitrate")) { + val bitrate = readableMap.getInt("bitrate") + hbRecorder?.setVideoBitrate(bitrate) + } } - } - if (doesSupportEncoder("h264")) { - hbRecorder!!.setVideoEncoder("H264"); - } else { - hbRecorder!!.setVideoEncoder("DEFAULT"); + if (doesSupportEncoder("h264")) { + hbRecorder?.setVideoEncoder("H264") + } else { + hbRecorder?.setVideoEncoder("DEFAULT") + } + hbRecorder?.isAudioEnabled(mic) + } catch (e: Exception) { + e.printStackTrace() } - hbRecorder!!.isAudioEnabled(mic); - reactApplicationContext.addActivityEventListener(mActivityEventListener); } private fun startRecordingScreen() { - val mediaProjectionManager = reactApplicationContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager; - val permissionIntent = mediaProjectionManager.createScreenCaptureIntent(); - currentActivity!!.startActivityForResult(permissionIntent, SCREEN_RECORD_REQUEST_CODE); + try { + val mediaProjectionManager = reactApplicationContext.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager + val permissionIntent = mediaProjectionManager.createScreenCaptureIntent() + + val currentActivity = reactApplicationContext.currentActivity + if (currentActivity != null) { + if (activityResultLauncher != null && currentActivity is AppCompatActivity) { + // Use modern ActivityResultLauncher for newer versions + activityResultLauncher?.launch(permissionIntent) + } else { + // Fallback to deprecated method for compatibility + @Suppress("DEPRECATION") + currentActivity.startActivityForResult(permissionIntent, SCREEN_RECORD_REQUEST_CODE) + } + } else { + startPromise?.reject("404", "No current activity available") + startPromise = null + } + } catch (e: Exception) { + startPromise?.reject("404", "Error starting screen recording: ${e.message}") + startPromise = null + } } - @ReactMethod fun startRecording(promise: Promise) { - startPromise = promise; + if (startPromise != null) { + promise.reject("409", "Recording already in progress") + return + } + + startPromise = promise + stopPromise = null + try { - startRecordingScreen(); - println("startRecording"); + // Create a new HBRecorder instance with a fresh listener + hbRecorder = HBRecorder(reactApplicationContext, object : HBRecorderListener { + override fun HBRecorderOnStart() { } + override fun HBRecorderOnPause() { } + override fun HBRecorderOnResume() { } + override fun HBRecorderOnComplete() { + stopPromise?.let { p -> + val uri = hbRecorder?.filePath ?: "" + saveVideoToGallery(uri, p) + } + stopPromise = null + startPromise = null + } + override fun HBRecorderOnError(errorCode: Int, reason: String?) { + val msg = reason ?: "Unknown" + startPromise?.reject("$errorCode", msg) + stopPromise?.reject("$errorCode", msg) + startPromise = null + stopPromise = null + } + }) + + outputUri?.let { uri -> hbRecorder?.setOutputPath(uri.toString()) } + hbRecorder?.isAudioEnabled(mic) + if (doesSupportEncoder("h264")) { + hbRecorder?.setVideoEncoder("H264") + } else { + hbRecorder?.setVideoEncoder("DEFAULT") + } + startRecordingScreen() } catch (e: IllegalStateException) { - promise.reject("404", "error!"); - println(e.toString()); + startPromise?.reject("404", "IllegalStateException: ${e.message}") + startPromise = null } catch (e: IOException) { - println(e); - e.printStackTrace(); - promise.reject("404", "error!!"); + e.printStackTrace() + startPromise?.reject("404", "IOException: ${e.message}") + startPromise = null + } catch (e: Exception) { + e.printStackTrace() + startPromise?.reject("404", "Error: ${e.message}") + startPromise = null } } @ReactMethod fun stopRecording(promise: Promise) { + if (stopPromise != null) { + promise.reject("409", "Stop recording already in progress") + return + } + + if (hbRecorder == null) { + promise.reject("404", "Recorder not initialized") + return + } + stopPromise = promise - hbRecorder!!.stopScreenRecording(); + + UiThreadUtil.runOnUiThread { + try { + hbRecorder?.stopScreenRecording() + } catch (e: Exception) { + stopPromise?.reject("404", "Error stopping recording: ${e.message}") + stopPromise = null + } + } + } + + private fun saveVideoToGallery(filePath: String, promise: Promise) { + try { + val file = File(filePath) + if (!file.exists()) { + promise.reject("404", "Recorded file not found: $filePath") + return + } + + val context = reactApplicationContext + val contentResolver = context.contentResolver + + // Generate a unique filename with timestamp + val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) + val displayName = "ScreenRecord_$timestamp.mp4" + + val contentValues = ContentValues().apply { + put(MediaStore.Video.Media.DISPLAY_NAME, displayName) + put(MediaStore.Video.Media.MIME_TYPE, "video/mp4") + put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put(MediaStore.Video.Media.IS_PENDING, 1) + } + } + + val uri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues) + + if (uri != null) { + val outputStream: OutputStream? = contentResolver.openOutputStream(uri) + val inputStream = FileInputStream(file) + + outputStream?.use { output -> + inputStream.use { input -> + input.copyTo(output) + } + } + + // Mark as not pending (Android Q+) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.clear() + contentValues.put(MediaStore.Video.Media.IS_PENDING, 0) + contentResolver.update(uri, contentValues, null, null) + } + + // Optional: Delete the original file from app directory + file.delete() + + val response = WritableNativeMap() + val result = WritableNativeMap() + result.putString("outputURL", uri.toString()) + result.putString("galleryPath", uri.toString()) + response.putString("status", "success") + response.putMap("result", result) + promise.resolve(response) + + } else { + promise.reject("404", "Failed to create gallery entry") + } + + } catch (e: Exception) { + promise.reject("404", "Error saving to gallery: ${e.message}") + } } @ReactMethod fun clean(promise: Promise) { - println("clean!!"); - println(outputUri); - outputUri!!.delete(); - promise.resolve("cleaned"); + try { + outputUri?.let { uri -> + if (uri.exists()) { + uri.listFiles()?.forEach { file -> + file.delete() + } + } + } + promise.resolve("cleaned") + } catch (e: Exception) { + promise.reject("404", "Error cleaning: ${e.message}") + } } override fun HBRecorderOnStart() { - println("HBRecorderOnStart") + // Recording started successfully } override fun HBRecorderOnComplete() { - println("HBRecorderOnComplete") - if (stopPromise != null) { - val uri = hbRecorder!!.filePath; - val response = WritableNativeMap(); - val result = WritableNativeMap(); - result.putString("outputURL", uri); - response.putString("status", "success"); - response.putMap("result", result); - stopPromise!!.resolve(response); + try { + stopPromise?.let { promise -> + val uri = hbRecorder?.filePath + if (uri != null) { + saveVideoToGallery(uri, promise) + } else { + promise.reject("404", "File path is null") + } + } + } catch (e: Exception) { + stopPromise?.reject("404", "Error on complete: ${e.message}") + } finally { + stopPromise = null + startPromise = null } } override fun HBRecorderOnError(errorCode: Int, reason: String?) { - println("HBRecorderOnError") - println("errorCode") - println(errorCode) - println("reason") - println(reason) + val errorMessage = "Error code: $errorCode, reason: ${reason ?: "Unknown"}" + + startPromise?.let { + it.reject("$errorCode", errorMessage) + startPromise = null + } + + stopPromise?.let { + it.reject("$errorCode", errorMessage) + stopPromise = null + } } override fun HBRecorderOnPause() { - println("HBRecorderOnPause") + // Recording paused } override fun HBRecorderOnResume() { - println("HBRecorderOnResume") + // Recording resumed } private fun doesSupportEncoder(encoder: String): Boolean { - val list = MediaCodecList(MediaCodecList.ALL_CODECS).codecInfos - val size = list.size - for (i in 0 until size) { - val codecInfo = list[i] - if (codecInfo.isEncoder) { - if (codecInfo!!.name.contains(encoder)) { - return true - } + return try { + val list = MediaCodecList(MediaCodecList.ALL_CODECS).codecInfos + list.any { codecInfo -> + codecInfo.isEncoder && codecInfo.name.contains(encoder, ignoreCase = true) } + } catch (e: Exception) { + false } - return false } -} + +} \ No newline at end of file diff --git a/android/src/main/java/com/recordscreen/RecordScreenPackage.kt b/android/src/main/java/com/recordscreen/RecordScreenPackage.kt index 6e8c32b..2990460 100644 --- a/android/src/main/java/com/recordscreen/RecordScreenPackage.kt +++ b/android/src/main/java/com/recordscreen/RecordScreenPackage.kt @@ -1,39 +1,16 @@ package com.recordscreen -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import com.facebook.react.ReactActivity -import java.util.Arrays -import java.util.Collections - import com.facebook.react.ReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager -import com.facebook.react.bridge.JavaScriptModule -class RecordScreenPackage : ReactPackage, AppCompatActivity() { +class RecordScreenPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List { - return Arrays.asList(RecordScreenModule(reactContext)) + return listOf(RecordScreenModule(reactContext)) } override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return emptyList>() + return emptyList() } - -// override fun onCreate(savedInstanceState: Bundle?) { -// super.onCreate(savedInstanceState); -// println("onCreate"); -// } -// override fun onStart() { -// super.onStart() -// } -// -// override fun onPause() { -// super.onPause() -// } -// -// override fun onStop() { -// super.onStop() -// } -} +} \ No newline at end of file diff --git a/ios/RecordScreen.h b/ios/RecordScreen.h index 1f79061..e8f7722 100644 --- a/ios/RecordScreen.h +++ b/ios/RecordScreen.h @@ -1,23 +1,12 @@ +// RecordScreen.h #import -#import -#import +#import -@interface RecordScreen : NSObject +@interface RecordScreen : RCTEventEmitter +@property (nonatomic, assign) int screenWidth; +@property (nonatomic, assign) int screenHeight; +@property (nonatomic, assign) BOOL enableMic; +@property (nonatomic, assign) int bitrate; +@property (nonatomic, assign) int fps; - @property (strong, nonatomic) RPScreenRecorder *screenRecorder; - @property (strong, nonatomic) AVAssetWriterInput *videoInput; - @property (strong, nonatomic) AVAssetWriterInput *audioInput; - @property (strong, nonatomic) AVAssetWriterInput *micInput; - @property (assign, nonatomic) int screenWidth; - @property (assign, nonatomic) int screenHeight; - @property (assign, nonatomic) BOOL enableMic; - @property (assign, nonatomic) int fps; - @property (assign, nonatomic) int bitrate; - - @property (nonatomic) AVAssetWriter *writer; - @property BOOL encounteredFirstBuffer; - @property CMSampleBufferRef afterAppBackgroundAudioSampleBuffer; - @property CMSampleBufferRef afterAppBackgroundMicSampleBuffer; - @property CMSampleBufferRef afterAppBackgroundVideoSampleBuffer; - -@end +@end \ No newline at end of file diff --git a/ios/RecordScreen.mm b/ios/RecordScreen.mm index 67ac51f..29e576d 100644 --- a/ios/RecordScreen.mm +++ b/ios/RecordScreen.mm @@ -1,301 +1,394 @@ +// RecordScreen.mm #import "RecordScreen.h" #import +#import +#import +#import + +@interface RecordScreen () +@property (nonatomic, strong) RPScreenRecorder *screenRecorder; +@property (nonatomic, strong) AVAssetWriter *writer; +@property (nonatomic, strong) AVAssetWriterInput *videoInput; +@property (nonatomic, strong) AVAssetWriterInput *audioInput; +@property (nonatomic, assign) BOOL isRecording; +@property (nonatomic, strong) NSString *outputPath; +@end @implementation RecordScreen -UIBackgroundTaskIdentifier _backgroundRenderingID; - -- (NSDictionary *)errorResponse:(NSDictionary *)result; -{ - NSDictionary *json = [NSDictionary dictionaryWithObjectsAndKeys: - @"error", @"status", - result, @"result",nil]; - return json; +UIBackgroundTaskIdentifier _backgroundTaskID = UIBackgroundTaskInvalid; +- (NSArray *)supportedEvents { + return @[]; } -- (NSDictionary *) successResponse:(NSDictionary *)result; -{ - NSDictionary *json = [NSDictionary dictionaryWithObjectsAndKeys: - @"success", @"status", - result, @"result",nil]; - return json; - -} - -- (void) muteAudioInBuffer:(CMSampleBufferRef)sampleBuffer -{ - - CMItemCount numSamples = CMSampleBufferGetNumSamples(sampleBuffer); - NSUInteger channelIndex = 0; - +- (void) muteAudioInBuffer:(CMSampleBufferRef)sampleBuffer { + if (!sampleBuffer) return; + CMBlockBufferRef audioBlockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); - size_t audioBlockBufferOffset = (channelIndex * numSamples * sizeof(SInt16)); - size_t lengthAtOffset = 0; + if (!audioBlockBuffer) return; + size_t totalLength = 0; SInt16 *samples = NULL; - CMBlockBufferGetDataPointer(audioBlockBuffer, audioBlockBufferOffset, &lengthAtOffset, &totalLength, (char **)(&samples)); - - for (NSInteger i=0; i)getTurboModule: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return std::make_shared(params); +RCT_REMAP_METHOD(isRecording, + isRecordingResolve:(RCTPromiseResolveBlock)resolve + isRecordingReject:(RCTPromiseRejectBlock)reject) { + BOOL isRecording = self.isRecording && self.screenRecorder && self.screenRecorder.isRecording; + resolve([NSNumber numberWithBool:isRecording]); } -#endif -@end +@end \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 1098c94..615ce6a 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,4 @@ -import { NativeModules, Dimensions } from 'react-native'; +import { NativeEventEmitter, NativeModules, Dimensions } from 'react-native'; export const RecordingResult = { Started: 'started', @@ -41,6 +41,10 @@ type RecordScreenNativeModule = { const { RecordScreen } = NativeModules; +if (!RecordScreen){ + throw new Error("RecordScreen native module is not linked"); + +} const RS = RecordScreen as RecordScreenNativeModule; class ReactNativeRecordScreenClass { @@ -54,7 +58,7 @@ class ReactNativeRecordScreenClass { bitrate: 1920 * 1080 * 144, ...config, }); - } + } startRecording(config: RecordScreenConfigType = {}) { this.setup(config);