stream-chat-react-native 9.0.0-beta.3 → 9.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -155,3 +155,33 @@ if (isNewArchitectureEnabled()) {
155
155
  codegenJavaPackageName = "com.streamchatreactnative"
156
156
  }
157
157
  }
158
+
159
+ if (isNewArchitectureEnabled()) {
160
+ gradle.projectsEvaluated {
161
+ if (rootProject.ext.has("streamChatReactNativeCodegenHookInstalled")) {
162
+ return
163
+ }
164
+
165
+ def androidAppProject = rootProject.subprojects.find { it.plugins.hasPlugin("com.android.application") }
166
+ if (androidAppProject == null) {
167
+ return
168
+ }
169
+
170
+ def dependencyCodegenTasks = rootProject.subprojects
171
+ .findAll { it != androidAppProject }
172
+ .collect { it.tasks.findByName("generateCodegenArtifactsFromSchema") }
173
+ .findAll { it != null }
174
+
175
+ if (dependencyCodegenTasks.isEmpty()) {
176
+ return
177
+ }
178
+
179
+ rootProject.ext.set("streamChatReactNativeCodegenHookInstalled", true)
180
+
181
+ androidAppProject.tasks.matching { task ->
182
+ task.name.startsWith("configureCMake")
183
+ }.configureEach {
184
+ dependsOn(dependencyCodegenTasks)
185
+ }
186
+ }
187
+ }
@@ -11,7 +11,6 @@ import android.net.Uri;
11
11
  import android.os.Build;
12
12
  import android.provider.MediaStore;
13
13
  import android.util.Base64;
14
- import android.util.Log;
15
14
 
16
15
  import java.io.ByteArrayOutputStream;
17
16
  import java.io.File;
@@ -33,108 +32,6 @@ public class StreamChatReactNative {
33
32
  private final static String SCHEME_FILE = "file";
34
33
  private final static String SCHEME_HTTP = "http";
35
34
  private final static String SCHEME_HTTPS = "https";
36
-
37
-
38
- // List of known EXIF tags we will be copying.
39
- // Orientation, width, height, and some others are ignored
40
- // TODO: Find any missing tag that might be useful
41
- private final static String[] EXIF_TO_COPY_ROTATED = new String[]
42
- {
43
- ExifInterface.TAG_APERTURE_VALUE,
44
- ExifInterface.TAG_MAX_APERTURE_VALUE,
45
- ExifInterface.TAG_METERING_MODE,
46
- ExifInterface.TAG_ARTIST,
47
- ExifInterface.TAG_BITS_PER_SAMPLE,
48
- ExifInterface.TAG_COMPRESSION,
49
- ExifInterface.TAG_BODY_SERIAL_NUMBER,
50
- ExifInterface.TAG_BRIGHTNESS_VALUE,
51
- ExifInterface.TAG_CONTRAST,
52
- ExifInterface.TAG_CAMERA_OWNER_NAME,
53
- ExifInterface.TAG_COLOR_SPACE,
54
- ExifInterface.TAG_COPYRIGHT,
55
- ExifInterface.TAG_DATETIME,
56
- ExifInterface.TAG_DATETIME_DIGITIZED,
57
- ExifInterface.TAG_DATETIME_ORIGINAL,
58
- ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION,
59
- ExifInterface.TAG_DIGITAL_ZOOM_RATIO,
60
- ExifInterface.TAG_EXIF_VERSION,
61
- ExifInterface.TAG_EXPOSURE_BIAS_VALUE,
62
- ExifInterface.TAG_EXPOSURE_INDEX,
63
- ExifInterface.TAG_EXPOSURE_MODE,
64
- ExifInterface.TAG_EXPOSURE_TIME,
65
- ExifInterface.TAG_EXPOSURE_PROGRAM,
66
- ExifInterface.TAG_FLASH,
67
- ExifInterface.TAG_FLASH_ENERGY,
68
- ExifInterface.TAG_FOCAL_LENGTH,
69
- ExifInterface.TAG_FOCAL_LENGTH_IN_35MM_FILM,
70
- ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT,
71
- ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION,
72
- ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION,
73
- ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION,
74
- ExifInterface.TAG_PLANAR_CONFIGURATION,
75
- ExifInterface.TAG_F_NUMBER,
76
- ExifInterface.TAG_GAIN_CONTROL,
77
- ExifInterface.TAG_GAMMA,
78
- ExifInterface.TAG_GPS_ALTITUDE,
79
- ExifInterface.TAG_GPS_ALTITUDE_REF,
80
- ExifInterface.TAG_GPS_AREA_INFORMATION,
81
- ExifInterface.TAG_GPS_DATESTAMP,
82
- ExifInterface.TAG_GPS_DOP,
83
- ExifInterface.TAG_GPS_LATITUDE,
84
- ExifInterface.TAG_GPS_LATITUDE_REF,
85
- ExifInterface.TAG_GPS_LONGITUDE,
86
- ExifInterface.TAG_GPS_LONGITUDE_REF,
87
- ExifInterface.TAG_GPS_STATUS,
88
- ExifInterface.TAG_GPS_DEST_BEARING,
89
- ExifInterface.TAG_GPS_DEST_BEARING_REF,
90
- ExifInterface.TAG_GPS_DEST_DISTANCE,
91
- ExifInterface.TAG_GPS_DEST_DISTANCE_REF,
92
- ExifInterface.TAG_GPS_DEST_LATITUDE,
93
- ExifInterface.TAG_GPS_DEST_LATITUDE_REF,
94
- ExifInterface.TAG_GPS_DEST_LONGITUDE,
95
- ExifInterface.TAG_GPS_DEST_LONGITUDE_REF,
96
- ExifInterface.TAG_GPS_DIFFERENTIAL,
97
- ExifInterface.TAG_GPS_IMG_DIRECTION,
98
- ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
99
- ExifInterface.TAG_GPS_MAP_DATUM,
100
- ExifInterface.TAG_GPS_MEASURE_MODE,
101
- ExifInterface.TAG_GPS_PROCESSING_METHOD,
102
- ExifInterface.TAG_GPS_SATELLITES,
103
- ExifInterface.TAG_GPS_SPEED,
104
- ExifInterface.TAG_GPS_SPEED_REF,
105
- ExifInterface.TAG_GPS_STATUS,
106
- ExifInterface.TAG_GPS_TIMESTAMP,
107
- ExifInterface.TAG_GPS_TRACK,
108
- ExifInterface.TAG_GPS_TRACK_REF,
109
- ExifInterface.TAG_GPS_VERSION_ID,
110
- ExifInterface.TAG_IMAGE_DESCRIPTION,
111
- ExifInterface.TAG_IMAGE_UNIQUE_ID,
112
- ExifInterface.TAG_ISO_SPEED,
113
- ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY,
114
- ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT,
115
- ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
116
- ExifInterface.TAG_LENS_MAKE,
117
- ExifInterface.TAG_LENS_MODEL,
118
- ExifInterface.TAG_LENS_SERIAL_NUMBER,
119
- ExifInterface.TAG_LENS_SPECIFICATION,
120
- ExifInterface.TAG_LIGHT_SOURCE,
121
- ExifInterface.TAG_MAKE,
122
- ExifInterface.TAG_MAKER_NOTE,
123
- ExifInterface.TAG_MODEL,
124
- // ExifInterface.TAG_ORIENTATION, // removed
125
- ExifInterface.TAG_SATURATION,
126
- ExifInterface.TAG_SHARPNESS,
127
- ExifInterface.TAG_SHUTTER_SPEED_VALUE,
128
- ExifInterface.TAG_SOFTWARE,
129
- ExifInterface.TAG_SUBJECT_DISTANCE,
130
- ExifInterface.TAG_SUBJECT_DISTANCE_RANGE,
131
- ExifInterface.TAG_SUBJECT_LOCATION,
132
- ExifInterface.TAG_USER_COMMENT,
133
- ExifInterface.TAG_WHITE_BALANCE
134
- };
135
-
136
-
137
-
138
35
  /**
139
36
  * Resize the specified bitmap.
140
37
  */
@@ -263,55 +160,6 @@ public class StreamChatReactNative {
263
160
  return file;
264
161
  }
265
162
 
266
- /**
267
- * Attempts to copy exif info from one file to another. Note: orientation, width, and height
268
- exif attributes are not copied since those are lost after image rotation.
269
-
270
- * imageUri: original image URI as provided from JS
271
- * dstPath: final image output path
272
- * Returns true if copy was successful, false otherwise.
273
- */
274
- public static boolean copyExif(Context context, Uri imageUri, String dstPath){
275
- ExifInterface src = null;
276
- ExifInterface dst = null;
277
-
278
- try {
279
-
280
- File file = getFileFromUri(context, imageUri);
281
- if (!file.exists()) {
282
- return false;
283
- }
284
-
285
- src = new ExifInterface(file.getAbsolutePath());
286
- dst = new ExifInterface(dstPath);
287
-
288
- } catch (Exception ignored) {
289
- Log.e("StreamChatReactNative::copyExif", "EXIF read failed", ignored);
290
- }
291
-
292
- if(src == null || dst == null){
293
- return false;
294
- }
295
-
296
- try{
297
-
298
- for (String attr : EXIF_TO_COPY_ROTATED)
299
- {
300
- String value = src.getAttribute(attr);
301
- if (value != null){
302
- dst.setAttribute(attr, value);
303
- }
304
- }
305
- dst.saveAttributes();
306
-
307
- } catch (Exception ignored) {
308
- Log.e("StreamChatReactNative::copyExif", "EXIF copy failed", ignored);
309
- return false;
310
- }
311
-
312
- return true;
313
- }
314
-
315
163
  /**
316
164
  * Get orientation by reading Image metadata
317
165
  */
@@ -591,4 +439,3 @@ public class StreamChatReactNative {
591
439
  return scaledImage;
592
440
  }
593
441
  }
594
-
@@ -4,8 +4,6 @@ import android.annotation.SuppressLint;
4
4
  import android.graphics.Bitmap;
5
5
  import android.net.Uri;
6
6
  import android.os.AsyncTask;
7
- import android.util.Log;
8
-
9
7
  import androidx.annotation.Nullable;
10
8
  import androidx.annotation.NonNull;
11
9
 
@@ -36,7 +34,7 @@ public class StreamChatReactNativeModule extends StreamChatReactNativeSpec {
36
34
  }
37
35
 
38
36
  @ReactMethod
39
- public void createResizedImage(String uri, double width, double height, String format, double quality, String mode, boolean onlyScaleDown, Double rotation, @Nullable String outputPath, Boolean keepMeta, Promise promise) {
37
+ public void createResizedImage(String uri, double width, double height, String format, double quality, String mode, boolean onlyScaleDown, Double rotation, @Nullable String outputPath, Promise promise) {
40
38
  WritableMap options = Arguments.createMap();
41
39
  options.putString("mode", mode);
42
40
  options.putBoolean("onlyScaleDown", onlyScaleDown);
@@ -46,7 +44,7 @@ public class StreamChatReactNativeModule extends StreamChatReactNativeSpec {
46
44
  @Override
47
45
  protected void doInBackgroundGuarded(Void... params) {
48
46
  try {
49
- Object response = createResizedImageWithExceptions(uri, (int) width, (int) height, format, (int) quality, rotation.intValue(), outputPath, keepMeta, options);
47
+ Object response = createResizedImageWithExceptions(uri, (int) width, (int) height, format, (int) quality, rotation.intValue(), outputPath, options);
50
48
  promise.resolve(response);
51
49
  }
52
50
  catch (IOException e) {
@@ -59,7 +57,6 @@ public class StreamChatReactNativeModule extends StreamChatReactNativeSpec {
59
57
  @SuppressLint("LongLogTag")
60
58
  private Object createResizedImageWithExceptions(String imagePath, int newWidth, int newHeight,
61
59
  String compressFormatString, int quality, int rotation, String outputPath,
62
- final boolean keepMeta,
63
60
  final ReadableMap options) throws IOException {
64
61
 
65
62
  Bitmap.CompressFormat compressFormat = Bitmap.CompressFormat.valueOf(compressFormatString);
@@ -89,16 +86,6 @@ public class StreamChatReactNativeModule extends StreamChatReactNativeSpec {
89
86
  response.putDouble("size", resizedImage.length());
90
87
  response.putDouble("width", scaledImage.getWidth());
91
88
  response.putDouble("height", scaledImage.getHeight());
92
-
93
- // Copy file's metadata/exif info if required
94
- if(keepMeta){
95
- try{
96
- StreamChatReactNative.copyExif(this.getReactApplicationContext(), imageUri, resizedImage.getAbsolutePath());
97
- }
98
- catch(Exception ignored){
99
- Log.e("StreamChatReactNative::createResizedImageWithExceptions", "EXIF copy failed", ignored);
100
- }
101
- }
102
89
  } else {
103
90
  throw new IOException("Error getting resized image path");
104
91
  }
@@ -14,12 +14,18 @@ import java.util.List;
14
14
  import java.util.Map;
15
15
 
16
16
  public class StreamChatReactNativePackage extends TurboReactPackage {
17
+ private static final String STREAM_VIDEO_THUMBNAIL_MODULE = "StreamVideoThumbnail";
17
18
 
18
19
  @Nullable
19
20
  @Override
20
21
  public NativeModule getModule(String name, ReactApplicationContext reactContext) {
21
22
  if (name.equals(StreamChatReactNativeModule.NAME)) {
22
23
  return new StreamChatReactNativeModule(reactContext);
24
+ } else if (name.equals(STREAM_VIDEO_THUMBNAIL_MODULE) && BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
25
+ return createNewArchModule(
26
+ "com.streamchatreactnative.StreamVideoThumbnailModule",
27
+ reactContext
28
+ );
23
29
  } else {
24
30
  return null;
25
31
  }
@@ -41,6 +47,17 @@ public class StreamChatReactNativePackage extends TurboReactPackage {
41
47
  false, // isCxxModule
42
48
  isTurboModule // isTurboModule
43
49
  ));
50
+ moduleInfos.put(
51
+ STREAM_VIDEO_THUMBNAIL_MODULE,
52
+ new ReactModuleInfo(
53
+ STREAM_VIDEO_THUMBNAIL_MODULE,
54
+ STREAM_VIDEO_THUMBNAIL_MODULE,
55
+ false, // canOverrideExistingModule
56
+ false, // needsEagerInit
57
+ false, // hasConstants
58
+ false, // isCxxModule
59
+ isTurboModule // isTurboModule
60
+ ));
44
61
  return moduleInfos;
45
62
  };
46
63
  }
@@ -49,4 +66,19 @@ public class StreamChatReactNativePackage extends TurboReactPackage {
49
66
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
50
67
  return Collections.<ViewManager>singletonList(new StreamShimmerViewManager());
51
68
  }
69
+
70
+ @Nullable
71
+ private NativeModule createNewArchModule(
72
+ String className,
73
+ ReactApplicationContext reactContext
74
+ ) {
75
+ try {
76
+ Class<?> moduleClass = Class.forName(className);
77
+ return (NativeModule) moduleClass
78
+ .getConstructor(ReactApplicationContext.class)
79
+ .newInstance(reactContext);
80
+ } catch (Throwable ignored) {
81
+ return null;
82
+ }
83
+ }
52
84
  }
@@ -0,0 +1,159 @@
1
+ package com.streamchatreactnative.shared
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.media.MediaMetadataRetriever
6
+ import android.net.Uri
7
+ import android.os.Build
8
+ import java.io.File
9
+ import java.io.FileOutputStream
10
+ import java.util.concurrent.Executors
11
+
12
+ data class StreamVideoThumbnailResult(
13
+ val error: String? = null,
14
+ val uri: String? = null,
15
+ )
16
+
17
+ object StreamVideoThumbnailGenerator {
18
+ private const val DEFAULT_COMPRESSION_QUALITY = 80
19
+ private const val DEFAULT_MAX_DIMENSION = 512
20
+ private const val CACHE_VERSION = "v1"
21
+ private const val CACHE_DIRECTORY_NAME = "@stream-io-stream-video-thumbnails"
22
+ private const val MAX_CONCURRENT_GENERATIONS = 5
23
+
24
+ fun generateThumbnails(context: Context, urls: List<String>): List<StreamVideoThumbnailResult> {
25
+ if (urls.size <= 1) {
26
+ return urls.map { url -> generateThumbnailResult(context, url) }
27
+ }
28
+
29
+ val parallelism = minOf(urls.size, MAX_CONCURRENT_GENERATIONS)
30
+ val executor = Executors.newFixedThreadPool(parallelism)
31
+
32
+ return try {
33
+ val tasks = urls.map { url ->
34
+ executor.submit<StreamVideoThumbnailResult> {
35
+ generateThumbnailResult(context, url)
36
+ }
37
+ }
38
+ tasks.map { task -> task.get() }
39
+ } finally {
40
+ executor.shutdown()
41
+ }
42
+ }
43
+
44
+ private fun generateThumbnailResult(context: Context, url: String): StreamVideoThumbnailResult {
45
+ return try {
46
+ StreamVideoThumbnailResult(uri = generateThumbnail(context, url))
47
+ } catch (error: Throwable) {
48
+ StreamVideoThumbnailResult(
49
+ error = error.message ?: "Thumbnail generation failed for $url",
50
+ uri = null,
51
+ )
52
+ }
53
+ }
54
+
55
+ private fun generateThumbnail(context: Context, url: String): String {
56
+ val outputDirectory = File(context.cacheDir, CACHE_DIRECTORY_NAME).apply { mkdirs() }
57
+ val outputFile = File(outputDirectory, buildCacheFileName(url))
58
+
59
+ if (outputFile.isFile() && outputFile.length() > 0L) {
60
+ return Uri.fromFile(outputFile).toString()
61
+ }
62
+
63
+ val retriever = MediaMetadataRetriever()
64
+
65
+ return try {
66
+ setDataSource(retriever, context, url)
67
+ val thumbnail = extractThumbnailFrame(retriever, url)
68
+
69
+ try {
70
+ FileOutputStream(outputFile).use { stream ->
71
+ thumbnail.compress(Bitmap.CompressFormat.JPEG, DEFAULT_COMPRESSION_QUALITY, stream)
72
+ }
73
+ } finally {
74
+ if (!thumbnail.isRecycled) {
75
+ thumbnail.recycle()
76
+ }
77
+ }
78
+
79
+ Uri.fromFile(outputFile).toString()
80
+ } catch (error: Throwable) {
81
+ throw IllegalStateException("Thumbnail generation failed for $url", error)
82
+ } finally {
83
+ try {
84
+ retriever.release()
85
+ } catch (_: Throwable) {
86
+ // Ignore cleanup failures.
87
+ }
88
+ }
89
+ }
90
+
91
+ private fun extractThumbnailFrame(retriever: MediaMetadataRetriever, url: String): Bitmap {
92
+ if (Build.VERSION.SDK_INT >= 27) {
93
+ return retriever.getScaledFrameAtTime(
94
+ 100000,
95
+ MediaMetadataRetriever.OPTION_CLOSEST_SYNC,
96
+ DEFAULT_MAX_DIMENSION,
97
+ DEFAULT_MAX_DIMENSION,
98
+ ) ?: throw IllegalStateException("Failed to extract video frame for $url")
99
+ }
100
+
101
+ val frame =
102
+ retriever.getFrameAtTime(100000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)
103
+ ?: throw IllegalStateException("Failed to extract video frame for $url")
104
+ val scaledFrame = scaleBitmap(frame)
105
+
106
+ if (scaledFrame != frame) {
107
+ frame.recycle()
108
+ }
109
+
110
+ return scaledFrame
111
+ }
112
+
113
+ private fun buildCacheFileName(url: String): String {
114
+ val cacheKey =
115
+ fnv1a64("$CACHE_VERSION|$DEFAULT_MAX_DIMENSION|$DEFAULT_COMPRESSION_QUALITY|$url")
116
+ return "stream-video-thumbnail-$cacheKey.jpg"
117
+ }
118
+
119
+ private fun fnv1a64(value: String): String {
120
+ var hash = -0x340d631b8c46751fL
121
+
122
+ value.toByteArray(Charsets.UTF_8).forEach { byte ->
123
+ hash = hash xor (byte.toLong() and 0xff)
124
+ hash *= 0x100000001b3L
125
+ }
126
+
127
+ return java.lang.Long.toUnsignedString(hash, 16)
128
+ }
129
+
130
+ private fun setDataSource(retriever: MediaMetadataRetriever, context: Context, url: String) {
131
+ val uri = Uri.parse(url)
132
+ val scheme = uri.scheme?.lowercase()
133
+
134
+ when {
135
+ scheme.isNullOrEmpty() -> retriever.setDataSource(url)
136
+ scheme == "content" || scheme == "file" -> retriever.setDataSource(context, uri)
137
+ else ->
138
+ throw IllegalArgumentException(
139
+ "Unsupported video URI scheme for thumbnail generation: $scheme. Local assets only.",
140
+ )
141
+ }
142
+ }
143
+
144
+ private fun scaleBitmap(bitmap: Bitmap): Bitmap {
145
+ val width = bitmap.width
146
+ val height = bitmap.height
147
+ val largestDimension = maxOf(width, height)
148
+
149
+ if (largestDimension <= DEFAULT_MAX_DIMENSION) {
150
+ return bitmap
151
+ }
152
+
153
+ val scale = DEFAULT_MAX_DIMENSION.toFloat() / largestDimension.toFloat()
154
+ val targetWidth = (width * scale).toInt().coerceAtLeast(1)
155
+ val targetHeight = (height * scale).toInt().coerceAtLeast(1)
156
+
157
+ return Bitmap.createScaledBitmap(bitmap, targetWidth, targetHeight, true)
158
+ }
159
+ }
@@ -0,0 +1,50 @@
1
+ package com.streamchatreactnative
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.ReadableArray
7
+ import com.streamchatreactnative.shared.StreamVideoThumbnailGenerator
8
+ import java.util.concurrent.Executors
9
+
10
+ class StreamVideoThumbnailModule(
11
+ reactContext: ReactApplicationContext,
12
+ ) : NativeStreamVideoThumbnailSpec(reactContext) {
13
+ override fun getName(): String = NAME
14
+
15
+ override fun createVideoThumbnails(urls: ReadableArray, promise: Promise) {
16
+ val urlList = mutableListOf<String>()
17
+ for (index in 0 until urls.size()) {
18
+ urlList.add(urls.getString(index) ?: "")
19
+ }
20
+
21
+ executor.execute {
22
+ try {
23
+ val thumbnails = StreamVideoThumbnailGenerator.generateThumbnails(reactApplicationContext, urlList)
24
+ val result = Arguments.createArray()
25
+ thumbnails.forEach { thumbnail ->
26
+ val thumbnailMap = Arguments.createMap()
27
+ if (thumbnail.uri != null) {
28
+ thumbnailMap.putString("uri", thumbnail.uri)
29
+ } else {
30
+ thumbnailMap.putNull("uri")
31
+ }
32
+ if (thumbnail.error != null) {
33
+ thumbnailMap.putString("error", thumbnail.error)
34
+ } else {
35
+ thumbnailMap.putNull("error")
36
+ }
37
+ result.pushMap(thumbnailMap)
38
+ }
39
+ promise.resolve(result)
40
+ } catch (error: Throwable) {
41
+ promise.reject("stream_video_thumbnail_error", error.message, error)
42
+ }
43
+ }
44
+ }
45
+
46
+ companion object {
47
+ const val NAME = "StreamVideoThumbnail"
48
+ private val executor = Executors.newCachedThreadPool()
49
+ }
50
+ }
@@ -12,5 +12,5 @@ abstract class StreamChatReactNativeSpec extends ReactContextBaseJavaModule {
12
12
  super(context);
13
13
  }
14
14
 
15
- public abstract void createResizedImage(String uri, double width, double height, String format, double quality, String mode, boolean onlyScaleDown, Double rotation, @Nullable String outputPath, Boolean keepMeta, Promise promise);
15
+ public abstract void createResizedImage(String uri, double width, double height, String format, double quality, String mode, boolean onlyScaleDown, Double rotation, @Nullable String outputPath, Promise promise);
16
16
  }