stream-chat-react-native 6.0.0-rc.2 → 6.0.0-rc.3

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.
@@ -0,0 +1,103 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:7.2.1"
9
+
10
+ }
11
+ }
12
+
13
+ def isNewArchitectureEnabled() {
14
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
15
+ }
16
+
17
+ apply plugin: "com.android.library"
18
+
19
+
20
+ def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
21
+
22
+ if (isNewArchitectureEnabled()) {
23
+ apply plugin: "com.facebook.react"
24
+ }
25
+
26
+ def getExtOrDefault(name) {
27
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ImageResizer_" + name]
28
+ }
29
+
30
+ def getExtOrIntegerDefault(name) {
31
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ImageResizer_" + name]).toInteger()
32
+ }
33
+
34
+ android {
35
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
36
+ def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
37
+ def agpMajorVersion = agpVersion.tokenize('.')[0].toInteger()
38
+ def agpMinorVersion = agpVersion.tokenize('.')[1].toInteger()
39
+ /**
40
+ * Namespace should be declared here starting from AGP 8.x, Starting AGP 7.3 it is also supported.
41
+ * For AGP < 7.3, namespace should be declared in AndroidManifest.
42
+ * See: https://developer.android.com/build/releases/past-releases/agp-8-0-0-release-notes#namespace-dsl
43
+ */
44
+ if (agpMajorVersion >= 7 && agpMinorVersion >= 3) {
45
+ namespace "com.streamchatreactnative"
46
+ }
47
+
48
+ defaultConfig {
49
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
50
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
51
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
52
+ }
53
+ buildTypes {
54
+ release {
55
+ minifyEnabled false
56
+ }
57
+ }
58
+
59
+ lintOptions {
60
+ disable "GradleCompatible"
61
+ }
62
+
63
+ compileOptions {
64
+ sourceCompatibility JavaVersion.VERSION_1_8
65
+ targetCompatibility JavaVersion.VERSION_1_8
66
+ }
67
+
68
+ sourceSets {
69
+ main {
70
+ if (isNewArchitectureEnabled()) {
71
+ java.srcDirs += [
72
+ "src/newarch",
73
+ // This is needed to build Kotlin project with NewArch enabled
74
+ "${project.buildDir}/generated/source/codegen/java"
75
+ ]
76
+ } else {
77
+ java.srcDirs += ["src/oldarch"]
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ repositories {
84
+ mavenCentral()
85
+ google()
86
+ }
87
+
88
+
89
+ dependencies {
90
+ // For < 0.71, this will be from the local maven repo
91
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
92
+ //noinspection GradleDynamicVersion
93
+ implementation "com.facebook.react:react-native:+"
94
+ implementation "androidx.exifinterface:exifinterface:1.3.2"
95
+ }
96
+
97
+ if (isNewArchitectureEnabled()) {
98
+ react {
99
+ jsRootDir = file("../src/")
100
+ libraryName = "StreamChatReactNative"
101
+ codegenJavaPackageName = "com.streamchatreactnative"
102
+ }
103
+ }
@@ -0,0 +1,5 @@
1
+ ImageResizer_kotlinVersion=1.7.0
2
+ ImageResizer_minSdkVersion=21
3
+ ImageResizer_targetSdkVersion=31
4
+ ImageResizer_compileSdkVersion=31
5
+ ImageResizer_ndkversion=21.4.7075529
@@ -0,0 +1,4 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.streamchatreactnative">
3
+
4
+ </manifest>
@@ -0,0 +1,594 @@
1
+ package com.streamchatreactnative;
2
+
3
+ import android.content.Context;
4
+ import android.content.ContentResolver;
5
+ import android.database.Cursor;
6
+ import android.graphics.Bitmap;
7
+ import android.graphics.BitmapFactory;
8
+ import android.graphics.Matrix;
9
+ import androidx.exifinterface.media.ExifInterface;
10
+ import android.net.Uri;
11
+ import android.os.Build;
12
+ import android.provider.MediaStore;
13
+ import android.util.Base64;
14
+ import android.util.Log;
15
+
16
+ import java.io.ByteArrayOutputStream;
17
+ import java.io.File;
18
+ import java.io.FileOutputStream;
19
+ import java.io.InputStream;
20
+ import java.io.IOException;
21
+ import java.net.HttpURLConnection;
22
+ import java.net.URL;
23
+ import java.util.Date;
24
+
25
+ /**
26
+ * Provide methods to resize and rotate an image file.
27
+ */
28
+ public class StreamChatReactNative {
29
+ private final static String IMAGE_JPEG = "image/jpeg";
30
+ private final static String IMAGE_PNG = "image/png";
31
+ private final static String SCHEME_DATA = "data";
32
+ private final static String SCHEME_CONTENT = "content";
33
+ private final static String SCHEME_FILE = "file";
34
+ private final static String SCHEME_HTTP = "http";
35
+ 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
+ /**
139
+ * Resize the specified bitmap.
140
+ */
141
+ private static Bitmap resizeImage(Bitmap image, int newWidth, int newHeight,
142
+ String mode, boolean onlyScaleDown) {
143
+ Bitmap newImage = null;
144
+ if (image == null) {
145
+ return null; // Can't load the image from the given path.
146
+ }
147
+
148
+ int width = image.getWidth();
149
+ int height = image.getHeight();
150
+
151
+ if (newHeight > 0 && newWidth > 0) {
152
+ int finalWidth;
153
+ int finalHeight;
154
+
155
+ if (mode.equals("stretch")) {
156
+ // Distort aspect ratio
157
+ finalWidth = newWidth;
158
+ finalHeight = newHeight;
159
+
160
+ if (onlyScaleDown) {
161
+ finalWidth = Math.min(width, finalWidth);
162
+ finalHeight = Math.min(height, finalHeight);
163
+ }
164
+ } else {
165
+ // "contain" (default) or "cover": keep its aspect ratio
166
+ float widthRatio = (float) newWidth / width;
167
+ float heightRatio = (float) newHeight / height;
168
+
169
+ float ratio = mode.equals("cover") ?
170
+ Math.max(widthRatio, heightRatio) :
171
+ Math.min(widthRatio, heightRatio);
172
+
173
+ if (onlyScaleDown) ratio = Math.min(ratio, 1);
174
+
175
+ finalWidth = (int) Math.round(width * ratio);
176
+ finalHeight = (int) Math.round(height * ratio);
177
+ }
178
+
179
+ try {
180
+ newImage = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);
181
+ } catch (OutOfMemoryError e) {
182
+ return null;
183
+ }
184
+ }
185
+
186
+ return newImage;
187
+ }
188
+
189
+ /**
190
+ * Rotate the specified bitmap with the given angle, in degrees.
191
+ */
192
+ public static Bitmap rotateImage(Bitmap source, Matrix matrix, float angle)
193
+ {
194
+ Bitmap retVal;
195
+ matrix.postRotate(angle);
196
+
197
+ try {
198
+ retVal = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
199
+ } catch (OutOfMemoryError e) {
200
+ return null;
201
+ }
202
+ return retVal;
203
+ }
204
+
205
+ /**
206
+ * Save the given bitmap in a directory. Extension is automatically generated using the bitmap format.
207
+ */
208
+ public static File saveImage(Bitmap bitmap, File saveDirectory, String fileName,
209
+ Bitmap.CompressFormat compressFormat, int quality)
210
+ throws IOException {
211
+ if (bitmap == null) {
212
+ throw new IOException("The bitmap couldn't be resized");
213
+ }
214
+
215
+ File newFile = new File(saveDirectory, fileName + "." + compressFormat.name());
216
+ if(!newFile.createNewFile()) {
217
+ throw new IOException("The file already exists");
218
+ }
219
+
220
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
221
+ bitmap.compress(compressFormat, quality, outputStream);
222
+ byte[] bitmapData = outputStream.toByteArray();
223
+
224
+ outputStream.flush();
225
+ outputStream.close();
226
+
227
+ FileOutputStream fos = new FileOutputStream(newFile);
228
+ fos.write(bitmapData);
229
+ fos.flush();
230
+ fos.close();
231
+
232
+ return newFile;
233
+ }
234
+
235
+ /**
236
+ * Get {@link File} object for the given Android URI.<br>
237
+ * Use content resolver to get real path if direct path doesn't return valid file.
238
+ */
239
+ private static File getFileFromUri(Context context, Uri uri) {
240
+
241
+ // first try by direct path
242
+ File file = new File(uri.getPath());
243
+ if (file.exists()) {
244
+ return file;
245
+ }
246
+
247
+ // try reading real path from content resolver (gallery images)
248
+ Cursor cursor = null;
249
+ try {
250
+ String[] proj = {MediaStore.Images.Media.DATA};
251
+ cursor = context.getContentResolver().query(uri, proj, null, null, null);
252
+ int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
253
+ cursor.moveToFirst();
254
+ String realPath = cursor.getString(column_index);
255
+ file = new File(realPath);
256
+ } catch (Exception ignored) {
257
+ } finally {
258
+ if (cursor != null) {
259
+ cursor.close();
260
+ }
261
+ }
262
+
263
+ return file;
264
+ }
265
+
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
+ /**
316
+ * Get orientation by reading Image metadata
317
+ */
318
+ public static Matrix getOrientationMatrix(Context context, Uri uri) {
319
+ try {
320
+ // ExifInterface(InputStream) only exists since Android N (r24)
321
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
322
+ InputStream input = context.getContentResolver().openInputStream(uri);
323
+ ExifInterface ei = new ExifInterface(input);
324
+ return getOrientationMatrix(ei);
325
+ }
326
+ File file = getFileFromUri(context, uri);
327
+ if (file.exists()) {
328
+ ExifInterface ei = new ExifInterface(file.getAbsolutePath());
329
+ return getOrientationMatrix(ei);
330
+ }
331
+ } catch (Exception ignored) { }
332
+
333
+ return new Matrix();
334
+ }
335
+
336
+ /**
337
+ * Convert metadata to degrees
338
+ */
339
+ public static Matrix getOrientationMatrix(ExifInterface exif) {
340
+ Matrix matrix = new Matrix();
341
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
342
+ switch (orientation) {
343
+ case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
344
+ matrix.setScale(-1, 1);
345
+ break;
346
+ case ExifInterface.ORIENTATION_TRANSPOSE:
347
+ matrix.setRotate(90);
348
+ matrix.postScale(-1, 1);
349
+ break;
350
+ case ExifInterface.ORIENTATION_ROTATE_90:
351
+ matrix.setRotate(90);
352
+ break;
353
+ case ExifInterface.ORIENTATION_FLIP_VERTICAL:
354
+ matrix.setRotate(180);
355
+ matrix.postScale(-1, 1);
356
+ break;
357
+ case ExifInterface.ORIENTATION_ROTATE_180:
358
+ matrix.setRotate(180);
359
+ break;
360
+ case ExifInterface.ORIENTATION_TRANSVERSE:
361
+ matrix.setRotate(270);
362
+ matrix.postScale(-1, 1);
363
+ break;
364
+ case ExifInterface.ORIENTATION_ROTATE_270:
365
+ matrix.setRotate(270);
366
+ break;
367
+ }
368
+ return matrix;
369
+ }
370
+
371
+ /**
372
+ * Compute the inSampleSize value to use to load a bitmap.
373
+ * Adapted from https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
374
+ */
375
+ private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
376
+ final int height = options.outHeight;
377
+ final int width = options.outWidth;
378
+
379
+ int inSampleSize = 1;
380
+
381
+ if (height > reqHeight || width > reqWidth) {
382
+ final int halfHeight = height / 2;
383
+ final int halfWidth = width / 2;
384
+
385
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
386
+ // height and width larger than the requested height and width.
387
+ while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
388
+ inSampleSize *= 2;
389
+ }
390
+ }
391
+
392
+ return inSampleSize;
393
+ }
394
+
395
+ /**
396
+ * Load a bitmap either from a real file or using the {@link ContentResolver} of the current
397
+ * {@link Context} (to read gallery images for example).
398
+ *
399
+ * Note that, when options.inJustDecodeBounds = true, we actually expect sourceImage to remain
400
+ * as null (see https://developer.android.com/training/displaying-bitmaps/load-bitmap.html), so
401
+ * getting null sourceImage at the completion of this method is not always worthy of an error.
402
+ */
403
+ private static Bitmap loadBitmap(Context context, Uri imageUri, BitmapFactory.Options options) throws IOException {
404
+ Bitmap sourceImage = null;
405
+ String imageUriScheme = imageUri.getScheme();
406
+ if (imageUriScheme == null || !imageUriScheme.equalsIgnoreCase(SCHEME_CONTENT)) {
407
+ try {
408
+ sourceImage = BitmapFactory.decodeFile(imageUri.getPath(), options);
409
+ } catch (Exception e) {
410
+ e.printStackTrace();
411
+ throw new IOException("Error decoding image file");
412
+ }
413
+ } else {
414
+ ContentResolver cr = context.getContentResolver();
415
+ InputStream input = cr.openInputStream(imageUri);
416
+ if (input != null) {
417
+ sourceImage = BitmapFactory.decodeStream(input, null, options);
418
+ input.close();
419
+ }
420
+ }
421
+ return sourceImage;
422
+ }
423
+
424
+ /**
425
+ * Loads the bitmap resource from the file specified in imagePath.
426
+ */
427
+ private static Bitmap loadBitmapFromFile(Context context, Uri imageUri, int newWidth,
428
+ int newHeight) throws IOException {
429
+ // Decode the image bounds to find the size of the source image.
430
+ BitmapFactory.Options options = new BitmapFactory.Options();
431
+ options.inJustDecodeBounds = true;
432
+ loadBitmap(context, imageUri, options);
433
+
434
+ // Set a sample size according to the image size to lower memory usage.
435
+ options.inSampleSize = calculateInSampleSize(options, newWidth, newHeight);
436
+ options.inJustDecodeBounds = false;
437
+ //System.out.println(options.inSampleSize);
438
+ return loadBitmap(context, imageUri, options);
439
+
440
+ }
441
+
442
+ /**
443
+ * Loads the bitmap resource from an URL
444
+ */
445
+ private static Bitmap loadBitmapFromURL(Uri imageUri, int newWidth,
446
+ int newHeight) throws IOException {
447
+
448
+ InputStream input = null;
449
+ Bitmap sourceImage = null;
450
+
451
+ try{
452
+ URL url = new URL(imageUri.toString());
453
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
454
+ connection.connect();
455
+ input = connection.getInputStream();
456
+
457
+ if (input != null) {
458
+
459
+ // need to load into memory since inputstream is not seekable
460
+ // we still won't load the whole bitmap into memory
461
+ // Also need this ugly code since we are on Java8...
462
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
463
+ int nRead;
464
+ byte[] data = new byte[1024];
465
+ byte[] imageData = null;
466
+
467
+ try{
468
+ while ((nRead = input.read(data, 0, data.length)) != -1) {
469
+ buffer.write(data, 0, nRead);
470
+ }
471
+ buffer.flush();
472
+ imageData = buffer.toByteArray();
473
+ }
474
+ finally{
475
+ buffer.close();
476
+ }
477
+
478
+
479
+ // Decode the image bounds to find the size of the source image.
480
+ // Do it here so we only do one request
481
+ BitmapFactory.Options options = new BitmapFactory.Options();
482
+ options.inJustDecodeBounds = true;
483
+ BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
484
+
485
+ // Set a sample size according to the image size to lower memory usage.
486
+ options.inSampleSize = calculateInSampleSize(options, newWidth, newHeight);
487
+ options.inJustDecodeBounds = false;
488
+
489
+ sourceImage = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
490
+ }
491
+ }
492
+ catch (Exception e) {
493
+ e.printStackTrace();
494
+ throw new IOException("Error fetching remote image file.");
495
+ }
496
+ finally{
497
+ try {
498
+ if(input != null){
499
+ input.close();
500
+ }
501
+ }
502
+ catch (IOException e) {
503
+ e.printStackTrace();
504
+ }
505
+
506
+ }
507
+
508
+ return sourceImage;
509
+
510
+ }
511
+
512
+ /**
513
+ * Loads the bitmap resource from a base64 encoded jpg or png.
514
+ * Format is as such:
515
+ * png: 'data:image/png;base64,iVBORw0KGgoAA...'
516
+ * jpg: 'data:image/jpeg;base64,/9j/4AAQSkZJ...'
517
+ */
518
+ private static Bitmap loadBitmapFromBase64(Uri imageUri) {
519
+ Bitmap sourceImage = null;
520
+ String imagePath = imageUri.getSchemeSpecificPart();
521
+ int commaLocation = imagePath.indexOf(',');
522
+ if (commaLocation != -1) {
523
+ final String mimeType = imagePath.substring(0, commaLocation).replace('\\','/').toLowerCase();
524
+ final boolean isJpeg = mimeType.startsWith(IMAGE_JPEG);
525
+ final boolean isPng = !isJpeg && mimeType.startsWith(IMAGE_PNG);
526
+
527
+ if (isJpeg || isPng) {
528
+ // base64 image. Convert to a bitmap.
529
+ final String encodedImage = imagePath.substring(commaLocation + 1);
530
+ final byte[] decodedString = Base64.decode(encodedImage, Base64.DEFAULT);
531
+ sourceImage = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
532
+ }
533
+ }
534
+
535
+ return sourceImage;
536
+ }
537
+
538
+ /**
539
+ * Create a resized version of the given image and returns a Bitmap object
540
+ * ready to be saved or converted. Ensure that the result is cleaned up after use
541
+ * by using recycle
542
+ */
543
+ public static Bitmap createResizedImage(Context context, Uri imageUri, int newWidth,
544
+ int newHeight, int quality, int rotation,
545
+ String mode, boolean onlyScaleDown) throws IOException {
546
+ Bitmap sourceImage = null;
547
+ String imageUriScheme = imageUri.getScheme();
548
+
549
+ if (imageUriScheme == null ||
550
+ imageUriScheme.equalsIgnoreCase(SCHEME_FILE) ||
551
+ imageUriScheme.equalsIgnoreCase(SCHEME_CONTENT)
552
+ ) {
553
+ sourceImage = StreamChatReactNative.loadBitmapFromFile(context, imageUri, newWidth, newHeight);
554
+ } else if (imageUriScheme.equalsIgnoreCase(SCHEME_HTTP) || imageUriScheme.equalsIgnoreCase(SCHEME_HTTPS)){
555
+ sourceImage = StreamChatReactNative.loadBitmapFromURL(imageUri, newWidth, newHeight);
556
+ } else if (imageUriScheme.equalsIgnoreCase(SCHEME_DATA)) {
557
+ sourceImage = StreamChatReactNative.loadBitmapFromBase64(imageUri);
558
+ }
559
+
560
+ if (sourceImage == null) {
561
+ throw new IOException("Unable to load source image from path");
562
+ }
563
+
564
+
565
+ // Rotate if necessary. Rotate first because we will otherwise
566
+ // get wrong dimensions if we want the new dimensions to be after rotation.
567
+ // NOTE: This will "fix" the image using it's exif info if it is rotated as well.
568
+ Bitmap rotatedImage = sourceImage;
569
+ Matrix matrix = getOrientationMatrix(context, imageUri);
570
+ rotatedImage = StreamChatReactNative.rotateImage(sourceImage, matrix, rotation);
571
+
572
+ if(rotatedImage == null){
573
+ throw new IOException("Unable to rotate image. Most likely due to not enough memory.");
574
+ }
575
+
576
+ if (rotatedImage != sourceImage) {
577
+ sourceImage.recycle();
578
+ }
579
+
580
+ // Scale image
581
+ Bitmap scaledImage = StreamChatReactNative.resizeImage(rotatedImage, newWidth, newHeight, mode, onlyScaleDown);
582
+
583
+ if(scaledImage == null){
584
+ throw new IOException("Unable to resize image. Most likely due to not enough memory.");
585
+ }
586
+
587
+ if (scaledImage != rotatedImage) {
588
+ rotatedImage.recycle();
589
+ }
590
+
591
+ return scaledImage;
592
+ }
593
+ }
594
+