diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index d787e503df26..cc1a130dd971 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -3046,11 +3046,16 @@ public class com/facebook/react/modules/blob/FileReaderModule : com/facebook/fbr public fun readAsText (Lcom/facebook/react/bridge/ReadableMap;Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V } -public class com/facebook/react/modules/camera/ImageStoreManager : com/facebook/fbreact/specs/NativeImageStoreAndroidSpec { +public final class com/facebook/react/modules/camera/ImageStoreManager : com/facebook/fbreact/specs/NativeImageStoreAndroidSpec { + public static final field Companion Lcom/facebook/react/modules/camera/ImageStoreManager$Companion; + public static final field NAME Ljava/lang/String; public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V public fun getBase64ForTag (Ljava/lang/String;Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V } +public final class com/facebook/react/modules/camera/ImageStoreManager$Companion { +} + public final class com/facebook/react/modules/clipboard/ClipboardModule : com/facebook/fbreact/specs/NativeClipboardSpec { public fun (Lcom/facebook/react/bridge/ReactApplicationContext;)V public fun getString (Lcom/facebook/react/bridge/Promise;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.java deleted file mode 100644 index 3acd0929f6a2..000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.modules.camera; - -import android.content.ContentResolver; -import android.net.Uri; -import android.os.AsyncTask; -import android.util.Base64; -import android.util.Base64OutputStream; -import com.facebook.fbreact.specs.NativeImageStoreAndroidSpec; -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.GuardedAsyncTask; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.module.annotations.ReactModule; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -@ReactModule(name = NativeImageStoreAndroidSpec.NAME) -public class ImageStoreManager extends NativeImageStoreAndroidSpec { - - private static final int BUFFER_SIZE = 8192; - - public ImageStoreManager(ReactApplicationContext reactContext) { - super(reactContext); - } - - /** - * Calculate the base64 representation for an image. The "tag" comes from iOS naming. - * - * @param uri the URI of the image, file:// or content:// - * @param success callback to be invoked with the base64 string as the only argument - * @param error callback to be invoked on error (e.g. file not found, not readable etc.) - */ - @Override - public void getBase64ForTag(String uri, Callback success, Callback error) { - new GetBase64Task(getReactApplicationContext(), uri, success, error) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private class GetBase64Task extends GuardedAsyncTask { - private final String mUri; - private final Callback mSuccess; - private final Callback mError; - - private GetBase64Task(ReactContext reactContext, String uri, Callback success, Callback error) { - super(reactContext); - mUri = uri; - mSuccess = success; - mError = error; - } - - @Override - protected void doInBackgroundGuarded(Void... params) { - try { - ContentResolver contentResolver = getReactApplicationContext().getContentResolver(); - Uri uri = Uri.parse(mUri); - InputStream is = contentResolver.openInputStream(uri); - try { - mSuccess.invoke(convertInputStreamToBase64OutputStream(is)); - } catch (IOException e) { - mError.invoke(e.getMessage()); - } finally { - closeQuietly(is); - } - } catch (FileNotFoundException e) { - mError.invoke(e.getMessage()); - } - } - } - - String convertInputStreamToBase64OutputStream(InputStream is) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Base64OutputStream b64os = new Base64OutputStream(baos, Base64.NO_WRAP); - byte[] buffer = new byte[BUFFER_SIZE]; - int bytesRead; - try { - while ((bytesRead = is.read(buffer)) > -1) { - b64os.write(buffer, 0, bytesRead); - } - } finally { - closeQuietly(b64os); // this also closes baos and flushes the final content to it - } - return baos.toString(); - } - - private static void closeQuietly(Closeable closeable) { - try { - closeable.close(); - } catch (IOException e) { - // shhh - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.kt new file mode 100644 index 000000000000..575b957ca682 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/camera/ImageStoreManager.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.modules.camera + +import android.net.Uri +import android.util.Base64 +import android.util.Base64OutputStream +import com.facebook.fbreact.specs.NativeImageStoreAndroidSpec +import com.facebook.react.bridge.Callback +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.module.annotations.ReactModule +import java.io.ByteArrayOutputStream +import java.io.Closeable +import java.io.FileNotFoundException +import java.io.IOException +import java.io.InputStream +import java.util.concurrent.Executors + +@ReactModule(name = NativeImageStoreAndroidSpec.NAME) +public class ImageStoreManager(reactContext: ReactApplicationContext) : + NativeImageStoreAndroidSpec(reactContext) { + + /** + * Calculate the base64 representation for an image. The "tag" comes from iOS naming. + * + * @param uri the URI of the image, file:// or content:// + * @param success callback to be invoked with the base64 string as the only argument + * @param error callback to be invoked on error (e.g. file not found, not readable etc.) + */ + override public fun getBase64ForTag(uri: String, success: Callback, error: Callback) { + val executor = Executors.newSingleThreadExecutor() + executor.execute { + try { + val contentResolver = getReactApplicationContext().getContentResolver() + val parsedUri = Uri.parse(uri) + val inputStream = contentResolver.openInputStream(parsedUri) as InputStream + try { + success.invoke(convertInputStreamToBase64OutputStream(inputStream)) + } catch (e: IOException) { + error.invoke(e.message) + } finally { + closeQuietly(inputStream) + } + } catch (e: FileNotFoundException) { + error.invoke(e.message) + } + } + } + + @Throws(IOException::class) + private fun convertInputStreamToBase64OutputStream(inputStream: InputStream): String { + val baos = ByteArrayOutputStream() + val b64os = Base64OutputStream(baos, Base64.NO_WRAP) + val buffer = ByteArray(BUFFER_SIZE) + var bytesRead: Int + try { + while (inputStream.read(buffer).also { bytesRead = it } > -1) { + b64os.write(buffer, 0, bytesRead) + } + } finally { + closeQuietly(b64os) // this also closes baos and flushes the final content to it + } + return baos.toString() + } + + public companion object { + public const val NAME: String = NativeImageStoreAndroidSpec.NAME + + private const val BUFFER_SIZE = 8_192 + + private fun closeQuietly(closeable: Closeable) { + try { + closeable.close() + } catch (e: IOException) { + // shhh + } + } + } +}