@@ -21,7 +21,18 @@ import android.provider.MediaStore
2121import android.text.TextUtils
2222import android.util.Base64
2323import androidx.exifinterface.media.ExifInterface
24+ import com.facebook.common.executors.CallerThreadExecutor
2425import com.facebook.common.logging.FLog
26+ import com.facebook.common.memory.PooledByteBuffer
27+ import com.facebook.common.memory.PooledByteBufferInputStream
28+ import com.facebook.common.references.CloseableReference
29+ import com.facebook.datasource.BaseDataSubscriber
30+ import com.facebook.datasource.DataSource
31+ import com.facebook.datasource.DataSubscriber
32+ import com.facebook.drawee.backends.pipeline.Fresco
33+ import com.facebook.imagepipeline.core.ImagePipeline
34+ import com.facebook.imagepipeline.request.ImageRequest
35+ import com.facebook.imagepipeline.request.ImageRequestBuilder
2536import com.facebook.infer.annotation.Assertions
2637import com.facebook.react.bridge.Arguments
2738import com.facebook.react.bridge.JSApplicationIllegalArgumentException
@@ -31,6 +42,8 @@ import com.facebook.react.bridge.ReadableMap
3142import com.facebook.react.bridge.ReadableType
3243import com.facebook.react.bridge.WritableMap
3344import com.facebook.react.common.ReactConstants
45+ import com.facebook.react.modules.fresco.ReactNetworkImageRequest
46+ import com.facebook.react.views.image.ReactCallerContextFactory
3447import java.io.ByteArrayInputStream
3548import java.io.File
3649import java.io.FileInputStream
@@ -51,7 +64,19 @@ object MimeType {
5164 const val WEBP = " image/webp"
5265}
5366
54- class ImageEditorModuleImpl (private val reactContext : ReactApplicationContext ) {
67+ interface ImageLoadCallback {
68+ fun onImageLoaded (inputStream : InputStream ? )
69+
70+ fun onFailure (error : Throwable )
71+ }
72+
73+ class ImageEditorModuleImpl (
74+ private val reactContext : ReactApplicationContext ,
75+ private val callerContext : Any? ,
76+ private val callerContextFactory : ReactCallerContextFactory ? ,
77+ private val imagePipeline : ImagePipeline ?
78+ ) {
79+
5580 private val moduleCoroutineScope = CoroutineScope (Dispatchers .Default )
5681
5782 init {
@@ -65,6 +90,58 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
6590 cleanTask()
6691 }
6792
93+ private fun getCallerContext (): Any? {
94+ return callerContextFactory?.getOrCreateCallerContext(" " , " " ) ? : callerContext
95+ }
96+
97+ private fun getImagePipeline (): ImagePipeline {
98+ return imagePipeline ? : Fresco .getImagePipeline()
99+ }
100+
101+ private fun fetchAndCacheImage (
102+ uri : String ,
103+ headers : ReadableMap ? ,
104+ callback : ImageLoadCallback
105+ ) {
106+ val uri = Uri .parse(uri)
107+ val imageRequest: ImageRequest =
108+ if (headers != null ) {
109+ val imageRequestBuilder = ImageRequestBuilder .newBuilderWithSource(uri)
110+ ReactNetworkImageRequest .fromBuilderWithHeaders(imageRequestBuilder, headers)
111+ } else ImageRequestBuilder .newBuilderWithSource(uri).build()
112+
113+ val dataSource: DataSource <CloseableReference <PooledByteBuffer >> =
114+ getImagePipeline().fetchEncodedImage(imageRequest, getCallerContext())
115+ val dataSubscriber: DataSubscriber <CloseableReference <PooledByteBuffer >> =
116+ object : BaseDataSubscriber <CloseableReference <PooledByteBuffer >>() {
117+ override fun onNewResultImpl (
118+ dataSource : DataSource <CloseableReference <PooledByteBuffer >>
119+ ) {
120+ if (! dataSource.isFinished()) {
121+ return
122+ }
123+ // getImagePipeline().isInBitmapMemoryCache(uri) // true
124+ // getImagePipeline().isInDiskCacheSync(uri) // true
125+ val ref = dataSource.getResult()
126+ val result = ref?.get()
127+ if (result != null ) {
128+ val stream: InputStream = PooledByteBufferInputStream (result)
129+ callback.onImageLoaded(stream)
130+ } else {
131+ callback.onImageLoaded(null )
132+ }
133+ }
134+
135+ override fun onFailureImpl (
136+ dataSource : DataSource <CloseableReference <PooledByteBuffer >>
137+ ) {
138+ dataSource.close()
139+ callback.onFailure(dataSource.failureCause!! )
140+ }
141+ }
142+ dataSource.subscribe(dataSubscriber, CallerThreadExecutor .getInstance())
143+ }
144+
68145 /* *
69146 * Asynchronous task that cleans up cache dirs (internal and, if available, external) of cropped
70147 * image files. This is run when the module is invalidated (i.e. app is shutting down) and when
@@ -148,7 +225,7 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
148225 // memory
149226 val hasTargetSize = targetWidth > 0 && targetHeight > 0
150227 val cropped: Bitmap ? =
151- if (hasTargetSize) {
228+ if (hasTargetSize)
152229 cropAndResizeTask(
153230 outOptions,
154231 uri,
@@ -160,9 +237,8 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
160237 targetHeight,
161238 headers
162239 )
163- } else {
164- cropTask(outOptions, uri, x, y, width, height, headers)
165- }
240+ else cropTask(outOptions, uri, x, y, width, height, headers)
241+
166242 if (cropped == null ) {
167243 throw IOException (" Cannot decode bitmap: $uri " )
168244 }
0 commit comments