@@ -21,7 +21,18 @@ import android.provider.MediaStore
21
21
import android.text.TextUtils
22
22
import android.util.Base64
23
23
import androidx.exifinterface.media.ExifInterface
24
+ import com.facebook.common.executors.CallerThreadExecutor
24
25
import 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
25
36
import com.facebook.infer.annotation.Assertions
26
37
import com.facebook.react.bridge.Arguments
27
38
import com.facebook.react.bridge.JSApplicationIllegalArgumentException
@@ -31,6 +42,8 @@ import com.facebook.react.bridge.ReadableMap
31
42
import com.facebook.react.bridge.ReadableType
32
43
import com.facebook.react.bridge.WritableMap
33
44
import com.facebook.react.common.ReactConstants
45
+ import com.facebook.react.modules.fresco.ReactNetworkImageRequest
46
+ import com.facebook.react.views.image.ReactCallerContextFactory
34
47
import java.io.ByteArrayInputStream
35
48
import java.io.File
36
49
import java.io.FileInputStream
@@ -51,7 +64,19 @@ object MimeType {
51
64
const val WEBP = " image/webp"
52
65
}
53
66
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
+
55
80
private val moduleCoroutineScope = CoroutineScope (Dispatchers .Default )
56
81
57
82
init {
@@ -65,6 +90,58 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
65
90
cleanTask()
66
91
}
67
92
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
+
68
145
/* *
69
146
* Asynchronous task that cleans up cache dirs (internal and, if available, external) of cropped
70
147
* 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) {
148
225
// memory
149
226
val hasTargetSize = targetWidth > 0 && targetHeight > 0
150
227
val cropped: Bitmap ? =
151
- if (hasTargetSize) {
228
+ if (hasTargetSize)
152
229
cropAndResizeTask(
153
230
outOptions,
154
231
uri,
@@ -160,9 +237,8 @@ class ImageEditorModuleImpl(private val reactContext: ReactApplicationContext) {
160
237
targetHeight,
161
238
headers
162
239
)
163
- } else {
164
- cropTask(outOptions, uri, x, y, width, height, headers)
165
- }
240
+ else cropTask(outOptions, uri, x, y, width, height, headers)
241
+
166
242
if (cropped == null ) {
167
243
throw IOException (" Cannot decode bitmap: $uri " )
168
244
}
0 commit comments