react-native-compressor 1.7.2 → 1.8.1

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.
Files changed (130) hide show
  1. package/README.md +51 -8
  2. package/android/build.gradle +9 -0
  3. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +527 -0
  4. package/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +31 -0
  5. package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +169 -0
  6. package/android/src/main/java/com/reactnativecompressor/CompressorPackage.kt +34 -0
  7. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.kt +199 -0
  8. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressorOptions.kt +57 -0
  9. package/android/src/main/java/com/reactnativecompressor/Image/ImageMain.kt +31 -0
  10. package/android/src/main/java/com/reactnativecompressor/Image/ImageSize.kt +3 -0
  11. package/android/src/main/java/com/reactnativecompressor/Utils/Downloader.kt +119 -0
  12. package/android/src/main/java/com/reactnativecompressor/Utils/EventEmitterHandler.kt +66 -0
  13. package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.kt +61 -0
  14. package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.kt +177 -0
  15. package/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +117 -0
  16. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +162 -0
  17. package/android/src/main/java/com/reactnativecompressor/Utils/createVideoThumbnail.kt +158 -0
  18. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression.kt +63 -0
  19. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.kt +125 -0
  20. package/android/src/main/java/com/reactnativecompressor/Video/VideoMain.kt +86 -0
  21. package/android/src/newarch/CompressorSpec.kt +5 -0
  22. package/android/src/oldarch/CompressorSpec.kt +34 -0
  23. package/ios/Audio/AudioMain.swift +115 -0
  24. package/ios/Compressor-Bridging-Header.h +1 -2
  25. package/ios/Compressor.h +0 -8
  26. package/ios/Compressor.mm +37 -389
  27. package/ios/CompressorManager.swift +107 -0
  28. package/ios/Image/ImageCompressor.swift +354 -0
  29. package/ios/Image/ImageCompressorOptions.swift +93 -0
  30. package/ios/Image/ImageMain.swift +27 -0
  31. package/ios/Utils/CreateVideoThumbnail.swift +111 -0
  32. package/ios/Utils/Downloader.swift +157 -0
  33. package/ios/Utils/EventEmitterHandler.swift +54 -0
  34. package/ios/Utils/MediaCache.swift +48 -0
  35. package/ios/Utils/Uploader.swift +99 -0
  36. package/ios/Utils/Utils.swift +110 -0
  37. package/ios/Video/{VideoCompressor.swift → VideoMain.swift} +167 -146
  38. package/lib/commonjs/Image/index.js +1 -1
  39. package/lib/commonjs/Image/index.js.map +1 -1
  40. package/lib/commonjs/Main.js +1 -8
  41. package/lib/commonjs/Main.js.map +1 -1
  42. package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
  43. package/lib/commonjs/Video/index.js +4 -33
  44. package/lib/commonjs/Video/index.js.map +1 -1
  45. package/lib/commonjs/expo-plugin/compressor.js.map +1 -1
  46. package/lib/commonjs/index.js +25 -6
  47. package/lib/commonjs/index.js.map +1 -1
  48. package/lib/commonjs/utils/Downloader.js +38 -0
  49. package/lib/commonjs/utils/Downloader.js.map +1 -0
  50. package/lib/commonjs/utils/Uploader.js +40 -0
  51. package/lib/commonjs/utils/Uploader.js.map +1 -0
  52. package/lib/commonjs/utils/index.js +47 -1
  53. package/lib/commonjs/utils/index.js.map +1 -1
  54. package/lib/module/Image/index.js +1 -1
  55. package/lib/module/Image/index.js.map +1 -1
  56. package/lib/module/Main.js +1 -7
  57. package/lib/module/Main.js.map +1 -1
  58. package/lib/module/Spec/NativeCompressor.js.map +1 -1
  59. package/lib/module/Video/index.js +5 -33
  60. package/lib/module/Video/index.js.map +1 -1
  61. package/lib/module/expo-plugin/compressor.js.map +1 -1
  62. package/lib/module/index.js +9 -7
  63. package/lib/module/index.js.map +1 -1
  64. package/lib/module/utils/Downloader.js +31 -0
  65. package/lib/module/utils/Downloader.js.map +1 -0
  66. package/lib/module/utils/Uploader.js +33 -0
  67. package/lib/module/utils/Uploader.js.map +1 -0
  68. package/lib/module/utils/index.js +9 -0
  69. package/lib/module/utils/index.js.map +1 -1
  70. package/lib/typescript/Image/index.d.ts +4 -0
  71. package/lib/typescript/Image/index.d.ts.map +1 -1
  72. package/lib/typescript/Main.d.ts +1 -2
  73. package/lib/typescript/Main.d.ts.map +1 -1
  74. package/lib/typescript/Spec/NativeCompressor.d.ts +16 -2
  75. package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
  76. package/lib/typescript/Video/index.d.ts +4 -28
  77. package/lib/typescript/Video/index.d.ts.map +1 -1
  78. package/lib/typescript/index.d.ts +7 -4
  79. package/lib/typescript/index.d.ts.map +1 -1
  80. package/lib/typescript/utils/Downloader.d.ts +2 -0
  81. package/lib/typescript/utils/Downloader.d.ts.map +1 -0
  82. package/lib/typescript/utils/Uploader.d.ts +28 -0
  83. package/lib/typescript/utils/Uploader.d.ts.map +1 -0
  84. package/lib/typescript/utils/index.d.ts +4 -1
  85. package/lib/typescript/utils/index.d.ts.map +1 -1
  86. package/package.json +6 -4
  87. package/src/Image/index.tsx +5 -1
  88. package/src/Main.tsx +1 -15
  89. package/src/Spec/NativeCompressor.ts +25 -2
  90. package/src/Video/index.tsx +11 -84
  91. package/src/expo-plugin/compressor.ts +1 -1
  92. package/src/index.tsx +12 -3
  93. package/src/utils/Downloader.tsx +38 -0
  94. package/src/utils/Uploader.tsx +78 -0
  95. package/src/utils/index.tsx +15 -0
  96. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.java +0 -605
  97. package/android/src/main/java/com/reactnativecompressor/CompressorModule.java +0 -220
  98. package/android/src/main/java/com/reactnativecompressor/CompressorPackage.java +0 -58
  99. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.java +0 -257
  100. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageCompressorOptions.java +0 -69
  101. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageSize.java +0 -13
  102. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploadHelper.java +0 -39
  103. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploader.java +0 -129
  104. package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.java +0 -65
  105. package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.java +0 -210
  106. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.java +0 -272
  107. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression/AutoVideoCompression.java +0 -87
  108. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.java +0 -182
  109. package/android/src/main/java/com/reactnativecompressor/Video/VideoModule.java +0 -112
  110. package/android/src/newarch/CompressorSpec.java +0 -9
  111. package/android/src/newarch/VideoCompressorSpec.java +0 -9
  112. package/android/src/oldarch/CompressorSpec.java +0 -34
  113. package/android/src/oldarch/VideoCompressorSpec.java +0 -26
  114. package/ios/Image/ImageCompressor.h +0 -22
  115. package/ios/Image/ImageCompressor.m +0 -540
  116. package/ios/Image/ImageCompressorOptions.h +0 -28
  117. package/ios/Image/ImageCompressorOptions.m +0 -97
  118. package/ios/Utils/Downloader.h +0 -16
  119. package/ios/Utils/Downloader.m +0 -150
  120. package/ios/Utils/MediaCache.h +0 -11
  121. package/ios/Utils/MediaCache.m +0 -51
  122. package/ios/Video/VideoCompressor.h +0 -5
  123. package/ios/Video/VideoCompressor.m +0 -1
  124. package/lib/commonjs/Spec/NativeVideoCompressor.js +0 -10
  125. package/lib/commonjs/Spec/NativeVideoCompressor.js.map +0 -1
  126. package/lib/module/Spec/NativeVideoCompressor.js +0 -3
  127. package/lib/module/Spec/NativeVideoCompressor.js.map +0 -1
  128. package/lib/typescript/Spec/NativeVideoCompressor.d.ts +0 -13
  129. package/lib/typescript/Spec/NativeVideoCompressor.d.ts.map +0 -1
  130. package/src/Spec/NativeVideoCompressor.ts +0 -14
@@ -0,0 +1,169 @@
1
+ package com.reactnativecompressor
2
+
3
+ import android.os.Build
4
+ import androidx.annotation.RequiresApi
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReactMethod
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.reactnativecompressor.Audio.AudioMain
10
+ import com.reactnativecompressor.Image.ImageMain
11
+ import com.reactnativecompressor.Utils.CreateVideoThumbnail
12
+ import com.reactnativecompressor.Utils.Downloader
13
+ import com.reactnativecompressor.Utils.EventEmitterHandler
14
+ import com.reactnativecompressor.Utils.Uploader
15
+ import com.reactnativecompressor.Utils.Utils
16
+ import com.reactnativecompressor.Utils.Utils.generateCacheFilePath
17
+ import com.reactnativecompressor.Utils.Utils.getRealPath
18
+ import com.reactnativecompressor.Video.VideoMain
19
+
20
+ class CompressorModule(private val reactContext: ReactApplicationContext) : CompressorSpec(reactContext) {
21
+ private val imageMain: ImageMain = ImageMain(reactContext)
22
+ private val videoMain: VideoMain = VideoMain(reactContext)
23
+ private val audioMain: AudioMain = AudioMain(reactContext)
24
+ private val videoThumbnail: CreateVideoThumbnail = CreateVideoThumbnail(reactContext)
25
+
26
+ override fun initialize() {
27
+ super.initialize()
28
+ EventEmitterHandler.reactContext=reactContext;
29
+ }
30
+
31
+
32
+ companion object {
33
+ const val NAME = "Compressor"
34
+ }
35
+ override fun getName(): String {
36
+ return NAME
37
+ }
38
+
39
+ //Image
40
+ @ReactMethod
41
+ override fun image_compress(
42
+ imagePath: String,
43
+ optionMap: ReadableMap,
44
+ promise: Promise) {
45
+ imageMain.image_compress(imagePath,optionMap,promise)
46
+ }
47
+
48
+ // VIdeo
49
+ @ReactMethod
50
+ override fun compress(
51
+ fileUrl: String,
52
+ optionMap: ReadableMap,
53
+ promise: Promise) {
54
+ videoMain.compress(fileUrl,optionMap,promise)
55
+ }
56
+
57
+ @ReactMethod
58
+ override fun cancelCompression(
59
+ uuid: String) {
60
+ videoMain.cancelCompression(uuid)
61
+ }
62
+
63
+ @ReactMethod
64
+ override fun activateBackgroundTask(
65
+ options: ReadableMap,
66
+ promise: Promise) {
67
+ videoMain.activateBackgroundTask(options,promise)
68
+ }
69
+
70
+ @ReactMethod
71
+ override fun deactivateBackgroundTask(
72
+ options: ReadableMap,
73
+ promise: Promise) {
74
+ videoMain.deactivateBackgroundTask(options,promise)
75
+ }
76
+
77
+ // Audio
78
+ @ReactMethod
79
+ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
80
+ override fun compress_audio(
81
+ fileUrl: String,
82
+ optionMap: ReadableMap,
83
+ promise: Promise) {
84
+ audioMain.compress_audio(fileUrl,optionMap,promise)
85
+ }
86
+
87
+ // Others
88
+ @ReactMethod
89
+ override fun generateFilePath(_extension: String, promise: Promise) {
90
+ try {
91
+ val outputUri = generateCacheFilePath(_extension, reactContext)
92
+ promise.resolve(outputUri)
93
+ } catch (e: Exception) {
94
+ promise.reject(e)
95
+ }
96
+ }
97
+
98
+ @ReactMethod
99
+ override fun getRealPath(path: String, type: String, promise: Promise) {
100
+ try {
101
+ val realPath = getRealPath(path, reactContext)
102
+ promise.resolve("file://$realPath")
103
+ } catch (e: Exception) {
104
+ promise.reject(e)
105
+ }
106
+ }
107
+
108
+ @ReactMethod
109
+ override fun getVideoMetaData(filePath: String, promise: Promise) {
110
+ videoMain.getVideoMetaData(filePath,promise)
111
+ }
112
+
113
+ @ReactMethod
114
+ override fun getFileSize(filePath: String, promise: Promise) {
115
+ Utils.getFileSize(filePath,promise,reactContext)
116
+ }
117
+
118
+ @ReactMethod
119
+ override fun upload(
120
+ fileUrl: String,
121
+ options: ReadableMap,
122
+ promise: Promise) {
123
+ Uploader.upload(fileUrl, options, reactContext, promise)
124
+ }
125
+
126
+ @ReactMethod
127
+ override fun download(
128
+ fileUrl: String,
129
+ options: ReadableMap,
130
+ promise: Promise) {
131
+ var uuid: String = ""
132
+ var progressDivider=0;
133
+ if(options.hasKey("uuid"))
134
+ {
135
+ uuid= options.getString("uuid") as String
136
+ }
137
+ if(options.hasKey("progressDivider"))
138
+ {
139
+ progressDivider= options.getString("progressDivider") as Int
140
+ }
141
+ val downloadedFilePath:String?=Downloader.downloadMediaWithProgress(fileUrl, uuid,progressDivider,reactContext)
142
+ if(downloadedFilePath!=null)
143
+ {
144
+ promise.resolve(downloadedFilePath)
145
+ }
146
+ else
147
+ {
148
+ promise.reject("Unable to download")
149
+ }
150
+ }
151
+
152
+ @ReactMethod
153
+ override fun createVideoThumbnail(fileUrl:String, options:ReadableMap, promise:Promise) {
154
+ videoThumbnail.create(fileUrl,options,promise)
155
+ }
156
+
157
+ @ReactMethod
158
+ override fun clearCache(cacheDir:String?, promise:Promise) {
159
+ CreateVideoThumbnail.clearCache(cacheDir, promise, reactContext)
160
+ }
161
+
162
+ @ReactMethod
163
+ override fun addListener(eventName: String) {
164
+ }
165
+
166
+ @ReactMethod
167
+ override fun removeListeners(count: Double) {
168
+ }
169
+ }
@@ -0,0 +1,34 @@
1
+ package com.reactnativecompressor
2
+
3
+ import com.facebook.react.TurboReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+
9
+ class CompressorPackage : TurboReactPackage() {
10
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
11
+ return if (name == CompressorModule.NAME) {
12
+ CompressorModule(reactContext)
13
+ } else {
14
+ null
15
+ }
16
+ }
17
+
18
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
19
+ return ReactModuleInfoProvider {
20
+ val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
21
+ val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
22
+ moduleInfos[CompressorModule.NAME] = ReactModuleInfo(
23
+ CompressorModule.NAME,
24
+ CompressorModule.NAME,
25
+ false, // canOverrideExistingModule
26
+ false, // needsEagerInit
27
+ true, // hasConstants
28
+ false, // isCxxModule
29
+ isTurboModule // isTurboModule
30
+ )
31
+ moduleInfos
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,199 @@
1
+ package com.reactnativecompressor.Image
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.Bitmap.CompressFormat
5
+ import android.graphics.BitmapFactory
6
+ import android.graphics.Canvas
7
+ import android.graphics.Matrix
8
+ import android.graphics.Paint
9
+ import android.media.ExifInterface
10
+ import android.net.Uri
11
+ import android.util.Base64
12
+ import com.facebook.react.bridge.ReactApplicationContext
13
+ import com.reactnativecompressor.Utils.Utils.generateCacheFilePath
14
+ import java.io.ByteArrayOutputStream
15
+ import java.io.File
16
+ import java.io.FileOutputStream
17
+ import java.io.IOException
18
+ import java.net.MalformedURLException
19
+
20
+ object ImageCompressor {
21
+ fun getRNFileUrl(filePath: String?): String? {
22
+ var filePath = filePath
23
+ val returnAbleFile = File(filePath)
24
+ try {
25
+ filePath = returnAbleFile.toURL().toString()
26
+ } catch (e: MalformedURLException) {
27
+ e.printStackTrace()
28
+ }
29
+ return filePath
30
+ }
31
+
32
+ fun findActualSize(image: Bitmap, maxWidth: Int, maxHeight: Int): ImageSize {
33
+ val width = image.width.toFloat()
34
+ val height = image.height.toFloat()
35
+ if (width > height) {
36
+ val newHeight = Math.round(height / (width / maxWidth))
37
+ val scale = newHeight / height
38
+ return ImageSize(maxWidth, newHeight, scale)
39
+ }
40
+ val newWidth = Math.round(width / (height / maxHeight))
41
+ val scale = newWidth / width
42
+ return ImageSize(newWidth, maxHeight, scale)
43
+ }
44
+
45
+ fun decodeImage(value: String?): Bitmap {
46
+ val data = Base64.decode(value, Base64.DEFAULT)
47
+ return BitmapFactory.decodeByteArray(data, 0, data.size)
48
+ }
49
+
50
+ fun loadImage(value: String?): Bitmap {
51
+ val uri = Uri.parse(value)
52
+ val filePath = uri.path
53
+ return BitmapFactory.decodeFile(filePath)
54
+ }
55
+
56
+ fun encodeImage(imageDataByteArrayOutputStream: ByteArrayOutputStream, isBase64: Boolean, outputExtension: String?, reactContext: ReactApplicationContext?): String? {
57
+ if (isBase64) {
58
+ val imageData = imageDataByteArrayOutputStream.toByteArray()
59
+ return Base64.encodeToString(imageData, Base64.DEFAULT)
60
+ } else {
61
+ val outputUri = generateCacheFilePath(outputExtension!!, reactContext!!)
62
+ try {
63
+ val fos = FileOutputStream(outputUri)
64
+ imageDataByteArrayOutputStream.writeTo(fos)
65
+ return getRNFileUrl(outputUri)
66
+ } catch (e: Exception) {
67
+ e.printStackTrace()
68
+ }
69
+ }
70
+ return ""
71
+ }
72
+
73
+ fun resize(image: Bitmap, maxWidth: Int, maxHeight: Int): Bitmap {
74
+ val size = findActualSize(image, maxWidth, maxHeight)
75
+ val scaledImage = Bitmap.createBitmap(size.width, size.height, image.config)
76
+ val scaleMatrix = Matrix()
77
+ val canvas = Canvas(scaledImage)
78
+ val paint = Paint(Paint.FILTER_BITMAP_FLAG)
79
+ scaleMatrix.setScale(size.scale, size.scale, 0f, 0f)
80
+ paint.isDither = true
81
+ paint.isAntiAlias = true
82
+ paint.isFilterBitmap = true
83
+ canvas.drawBitmap(image, scaleMatrix, paint)
84
+ return scaledImage
85
+ }
86
+
87
+ fun compress(image: Bitmap?, output: ImageCompressorOptions.OutputType, quality: Float): ByteArrayOutputStream {
88
+ val stream = ByteArrayOutputStream()
89
+ val format = if (output === ImageCompressorOptions.OutputType.jpg) CompressFormat.JPEG else CompressFormat.PNG
90
+ image!!.compress(format, Math.round(100 * quality), stream)
91
+ return stream
92
+ }
93
+
94
+ fun manualCompressImage(imagePath: String?, options: ImageCompressorOptions, reactContext: ReactApplicationContext?): String? {
95
+ val image = if (options.input === ImageCompressorOptions.InputType.base64) decodeImage(imagePath) else loadImage(imagePath)
96
+ val resizedImage = resize(image, options.maxWidth, options.maxHeight)
97
+ val imageDataByteArrayOutputStream = compress(resizedImage, options.output, options.quality)
98
+ val isBase64 = options.returnableOutputType === ImageCompressorOptions.ReturnableOutputType.base64
99
+ return encodeImage(imageDataByteArrayOutputStream, isBase64, options.output.toString(), reactContext)
100
+ }
101
+
102
+ fun autoCompressImage(imagePath: String?, compressorOptions: ImageCompressorOptions, reactContext: ReactApplicationContext?): String? {
103
+ var imagePath = imagePath
104
+ val outputExtension = compressorOptions.output.toString()
105
+ val autoCompressMaxHeight = compressorOptions.maxHeight.toFloat()
106
+ val autoCompressMaxWidth = compressorOptions.maxWidth.toFloat()
107
+ val isBase64 = compressorOptions.returnableOutputType === ImageCompressorOptions.ReturnableOutputType.base64
108
+ val uri = Uri.parse(imagePath)
109
+ imagePath = uri.path
110
+ var scaledBitmap: Bitmap? = null
111
+ val options = BitmapFactory.Options()
112
+ options.inJustDecodeBounds = true
113
+ var bmp = BitmapFactory.decodeFile(imagePath, options)
114
+ var actualHeight = options.outHeight
115
+ var actualWidth = options.outWidth
116
+ var imgRatio = actualWidth.toFloat() / actualHeight.toFloat()
117
+ val maxRatio = autoCompressMaxWidth / autoCompressMaxHeight
118
+ if (actualHeight > autoCompressMaxHeight || actualWidth > autoCompressMaxWidth) {
119
+ if (imgRatio < maxRatio) {
120
+ imgRatio = autoCompressMaxHeight / actualHeight
121
+ actualWidth = (imgRatio * actualWidth).toInt()
122
+ actualHeight = autoCompressMaxHeight.toInt()
123
+ } else if (imgRatio > maxRatio) {
124
+ imgRatio = autoCompressMaxWidth / actualWidth
125
+ actualHeight = (imgRatio * actualHeight).toInt()
126
+ actualWidth = autoCompressMaxWidth.toInt()
127
+ } else {
128
+ actualHeight = autoCompressMaxHeight.toInt()
129
+ actualWidth = autoCompressMaxWidth.toInt()
130
+ }
131
+ }
132
+ options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)
133
+ options.inJustDecodeBounds = false
134
+ options.inDither = false
135
+ options.inPurgeable = true
136
+ options.inInputShareable = true
137
+ options.inTempStorage = ByteArray(16 * 1024)
138
+ try {
139
+ bmp = BitmapFactory.decodeFile(imagePath, options)
140
+ } catch (exception: OutOfMemoryError) {
141
+ exception.printStackTrace()
142
+ }
143
+ try {
144
+ scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565)
145
+ } catch (exception: OutOfMemoryError) {
146
+ exception.printStackTrace()
147
+ }
148
+ val ratioX = actualWidth / options.outWidth.toFloat()
149
+ val ratioY = actualHeight / options.outHeight.toFloat()
150
+ val middleX = actualWidth / 2.0f
151
+ val middleY = actualHeight / 2.0f
152
+ val scaleMatrix = Matrix()
153
+ scaleMatrix.setScale(ratioX, ratioY, middleX, middleY)
154
+ val canvas = Canvas(scaledBitmap!!)
155
+ canvas.setMatrix(scaleMatrix)
156
+ canvas.drawBitmap(bmp!!, middleX - bmp.width / 2, middleY - bmp.height / 2, Paint(Paint.FILTER_BITMAP_FLAG))
157
+ if (bmp != null) {
158
+ bmp.recycle()
159
+ }
160
+ scaledBitmap = correctImageOrientation(scaledBitmap, imagePath)
161
+ val imageDataByteArrayOutputStream = compress(scaledBitmap, compressorOptions.output, compressorOptions.quality)
162
+ return encodeImage(imageDataByteArrayOutputStream, isBase64, compressorOptions.output.toString(), reactContext)
163
+ }
164
+
165
+ fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
166
+ val height = options.outHeight
167
+ val width = options.outWidth
168
+ var inSampleSize = 1
169
+ if (height > reqHeight || width > reqWidth) {
170
+ val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat())
171
+ val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat())
172
+ inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
173
+ }
174
+ val totalPixels = (width * height).toFloat()
175
+ val totalReqPixelsCap = (reqWidth * reqHeight * 2).toFloat()
176
+ while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
177
+ inSampleSize++
178
+ }
179
+ return inSampleSize
180
+ }
181
+
182
+ fun correctImageOrientation(bitmap: Bitmap?, imagePath: String?): Bitmap? {
183
+ return try {
184
+ val exif = ExifInterface(imagePath!!)
185
+ val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
186
+ val matrix = Matrix()
187
+ when (orientation) {
188
+ ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
189
+ ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
190
+ ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
191
+ else -> return bitmap // No rotation needed
192
+ }
193
+ Bitmap.createBitmap(bitmap!!, 0, 0, bitmap.width, bitmap.height, matrix, true)
194
+ } catch (e: IOException) {
195
+ e.printStackTrace()
196
+ bitmap // Return original bitmap if an error occurs
197
+ }
198
+ }
199
+ }
@@ -0,0 +1,57 @@
1
+ package com.reactnativecompressor.Image
2
+
3
+ import com.facebook.react.bridge.ReadableMap
4
+
5
+ class ImageCompressorOptions {
6
+ enum class InputType {
7
+ base64,
8
+ uri
9
+ }
10
+
11
+ enum class OutputType {
12
+ png,
13
+ jpg
14
+ }
15
+
16
+ enum class ReturnableOutputType {
17
+ base64,
18
+ uri
19
+ }
20
+
21
+ enum class CompressionMethod {
22
+ auto,
23
+ manual
24
+ }
25
+
26
+ var compressionMethod = CompressionMethod.auto
27
+ var maxWidth = 1280
28
+ var maxHeight = 1280
29
+ var progressDivider: Int? = 0
30
+ var quality = 0.8f
31
+ var input = InputType.uri
32
+ var output = OutputType.jpg
33
+ var uuid: String? = ""
34
+ var returnableOutputType = ReturnableOutputType.uri
35
+
36
+ companion object {
37
+ fun fromMap(map: ReadableMap): ImageCompressorOptions {
38
+ val options = ImageCompressorOptions()
39
+ val iterator = map.keySetIterator()
40
+ while (iterator.hasNextKey()) {
41
+ val key = iterator.nextKey()
42
+ when (key) {
43
+ "compressionMethod" -> options.compressionMethod = CompressionMethod.valueOf(map.getString(key)!!)
44
+ "maxWidth" -> options.maxWidth = map.getInt(key)
45
+ "maxHeight" -> options.maxHeight = map.getInt(key)
46
+ "progressDivider" -> options.progressDivider = map.getInt(key)
47
+ "quality" -> options.quality = map.getDouble(key).toFloat()
48
+ "input" -> options.input = InputType.valueOf(map.getString(key)!!)
49
+ "output" -> options.output = OutputType.valueOf(map.getString(key)!!)
50
+ "returnableOutputType" -> options.returnableOutputType = ReturnableOutputType.valueOf(map.getString(key)!!)
51
+ "uuid" -> options.uuid = map.getString(key)
52
+ }
53
+ }
54
+ return options
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,31 @@
1
+ package com.reactnativecompressor.Image
2
+
3
+ import com.facebook.react.bridge.Promise
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReadableMap
6
+ import com.reactnativecompressor.Utils.MediaCache
7
+ import com.reactnativecompressor.Utils.Utils
8
+
9
+ class ImageMain(private val reactContext: ReactApplicationContext) {
10
+ fun image_compress(
11
+ imagePath: String,
12
+ optionMap: ReadableMap,
13
+ promise: Promise
14
+ ) {
15
+ var imagePath: String? = imagePath
16
+ try {
17
+ val options = ImageCompressorOptions.fromMap(optionMap)
18
+ imagePath = Utils.getRealPath(imagePath, reactContext, options.uuid, options.progressDivider)
19
+ if (options.compressionMethod === ImageCompressorOptions.CompressionMethod.auto) {
20
+ val returnableResult = ImageCompressor.autoCompressImage(imagePath, options, reactContext)
21
+ promise.resolve(returnableResult)
22
+ } else {
23
+ val returnableResult = ImageCompressor.manualCompressImage(imagePath, options, reactContext)
24
+ promise.resolve(returnableResult)
25
+ }
26
+ MediaCache.removeCompletedImagePath(imagePath)
27
+ } catch (ex: Exception) {
28
+ promise.reject(ex)
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,3 @@
1
+ package com.reactnativecompressor.Image
2
+
3
+ class ImageSize(@JvmField var width: Int, @JvmField var height: Int, @JvmField var scale: Float)
@@ -0,0 +1,119 @@
1
+ package com.reactnativecompressor.Utils
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import okhttp3.Call
6
+ import okhttp3.Callback
7
+ import okhttp3.OkHttpClient
8
+ import okhttp3.Request
9
+ import okhttp3.Response
10
+ import java.io.BufferedInputStream
11
+ import java.io.File
12
+ import java.io.FileOutputStream
13
+ import java.io.IOException
14
+ import java.util.UUID
15
+ import java.util.concurrent.Semaphore
16
+ import java.util.concurrent.atomic.AtomicReference
17
+
18
+ class Downloader {
19
+ companion object {
20
+ private const val TAG = "react-native-compessor"
21
+
22
+ fun downloadMediaWithProgress(mediaUrl: String?, uuid: String,progressDivider:Int, reactContext: ReactApplicationContext): String? {
23
+ downloadCompression[0] = 0
24
+ val client = OkHttpClient()
25
+ val request: Request = Request.Builder()
26
+ .url(mediaUrl!!)
27
+ .build()
28
+ val semaphore = Semaphore(0) // Semaphore to wait for the download to complete
29
+ val filePathRef = AtomicReference<String?>(null) // To store the file path
30
+
31
+ // Perform the request asynchronously
32
+ client.newCall(request).enqueue(object : Callback {
33
+ @Throws(IOException::class)
34
+ override fun onResponse(call: Call, response: Response) {
35
+ if (response.isSuccessful) {
36
+ val responseBody = response.body
37
+ if (responseBody != null) {
38
+ var fileExtension = "unknown" // Default extension
39
+
40
+ // Detect the file extension based on the Content-Type header
41
+ val contentType = response.header("Content-Type")
42
+ if (contentType != null) {
43
+ if (contentType == "image/jpeg") {
44
+ fileExtension = "jpg"
45
+ } else if (contentType == "image/png") {
46
+ fileExtension = "png"
47
+ } else if (contentType == "video/mp4") {
48
+ fileExtension = "mp4"
49
+ }
50
+ }
51
+ val cacheDir = reactContext.cacheDir
52
+ val randomFileName = UUID.randomUUID().toString() + "." + fileExtension
53
+ val mediaFile = File(cacheDir, randomFileName)
54
+ try {
55
+ FileOutputStream(mediaFile).use { fos ->
56
+ val inputStream = BufferedInputStream(responseBody.byteStream())
57
+ val buffer = ByteArray(4096)
58
+ var bytesRead: Int
59
+ var totalBytesRead: Long = 0
60
+ var totalBytes = responseBody.contentLength()
61
+ if (totalBytes <= 0) {
62
+ totalBytes = 31457280
63
+ }
64
+ Log.d(TAG, "$totalBytesRead totalBytesRead $totalBytes")
65
+ Log.d(TAG, "$response responseBody $responseBody")
66
+ while (inputStream.read(buffer).also { bytesRead = it } != -1) {
67
+ fos.write(buffer, 0, bytesRead)
68
+ totalBytesRead += bytesRead.toLong()
69
+ val progressRatio = totalBytesRead.toDouble() / totalBytes
70
+ val progress = (progressRatio * 100).toInt()
71
+ sendProgressUpdate(progress, uuid, progressDivider)
72
+ }
73
+ fos.flush()
74
+ val filePath = "file://" + mediaFile.absolutePath
75
+ MediaCache.addCompletedImagePath(filePath)
76
+ filePathRef.set(filePath) // Set the file path
77
+ }
78
+ } catch (e: IOException) {
79
+ e.printStackTrace()
80
+ sendErrorResult(e.message, uuid)
81
+ } finally {
82
+ semaphore.release() // Release the semaphore when download is complete
83
+ }
84
+ }
85
+ } else {
86
+ sendErrorResult("Failed to download media: " + response.message, uuid)
87
+ semaphore.release() // Release the semaphore in case of error
88
+ }
89
+ }
90
+
91
+ override fun onFailure(call: Call, e: IOException) {
92
+ e.printStackTrace()
93
+ sendErrorResult(e.message, uuid)
94
+ semaphore.release() // Release the semaphore in case of failure
95
+ }
96
+ })
97
+ try {
98
+ semaphore.acquire() // Wait for the download to complete
99
+ } catch (e: InterruptedException) {
100
+ e.printStackTrace()
101
+ }
102
+ return filePathRef.get() // Return the file path
103
+ }
104
+
105
+ val downloadCompression = intArrayOf(0)
106
+ private fun sendProgressUpdate(progress: Int, uuid: String, progressDivider:Int=0) {
107
+ val roundProgress = Math.round(progress.toFloat())
108
+ if (progressDivider==0||(roundProgress % progressDivider == 0 && roundProgress > downloadCompression[0])) {
109
+ EventEmitterHandler.emitDownloadProgress(progress / 100.0,uuid)
110
+ Log.d(TAG, "downloadProgress: " + progress / 100.0)
111
+ downloadCompression[0] = roundProgress
112
+ }
113
+ }
114
+
115
+ private fun sendErrorResult(error: String?, uuid: String?) {
116
+ EventEmitterHandler.emitDownloadProgressError(uuid,error)
117
+ }
118
+ }
119
+ }
@@ -0,0 +1,66 @@
1
+ package com.reactnativecompressor.Utils
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.modules.core.DeviceEventManagerModule
6
+
7
+ class EventEmitterHandler {
8
+ companion object {
9
+ public var reactContext: ReactApplicationContext?=null
10
+
11
+ private fun sendEvent(eventName: String,
12
+ params: Any?) {
13
+ reactContext
14
+ ?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
15
+ ?.emit(eventName, params)
16
+ }
17
+
18
+ fun emitBackgroundTaskExpired(backgroundId: String?){
19
+ sendEvent("backgroundTaskExpired",backgroundId)
20
+ }
21
+
22
+ fun emitVideoCompressProgress(progress:Double,uuid:String){
23
+ val params = Arguments.createMap()
24
+ val data = Arguments.createMap()
25
+ params.putString("uuid", uuid)
26
+ data.putDouble("progress", progress)
27
+ params.putMap("data", data)
28
+ sendEvent("videoCompressProgress", params)
29
+ }
30
+
31
+ fun emitDownloadProgress(progress:Double,uuid:String){
32
+ val params = Arguments.createMap()
33
+ val data = Arguments.createMap()
34
+ params.putString("uuid", uuid)
35
+ data.putDouble("progress", progress)
36
+ params.putMap("data", data)
37
+ sendEvent("downloadProgress", params)
38
+ }
39
+
40
+ fun emitDownloadProgressError(uuid:String?, error:String?){
41
+ if (uuid != null && error != null) {
42
+ val params = Arguments.createMap()
43
+ val data = Arguments.createMap()
44
+ params.putString("uuid", uuid)
45
+ params.putString("error", error)
46
+ params.putMap("data", data)
47
+ sendEvent("downloadProgressError", params)
48
+ }
49
+ }
50
+
51
+ fun sendUploadProgressEvent(numBytes: Long, totalBytes: Long, uuid:String?) {
52
+ if(uuid!=null) {
53
+ val _params = Arguments.createMap()
54
+ val _data = Arguments.createMap()
55
+ _params.putString("uuid", uuid)
56
+ _data.putDouble("written", numBytes.toDouble())
57
+ _data.putDouble("total", totalBytes.toDouble())
58
+ _params.putMap("data", _data)
59
+ sendEvent("uploadProgress", _params)
60
+ }
61
+ }
62
+
63
+
64
+ }
65
+
66
+ }