1
1
package com.margelo.nitro.multipleimagepicker
2
2
3
+ import android.app.Activity
4
+ import android.content.ContentResolver
3
5
import android.content.Context
6
+ import android.content.Intent
4
7
import android.graphics.Color
8
+ import android.net.Uri
5
9
import androidx.core.content.ContextCompat
10
+ import com.facebook.react.bridge.ActivityEventListener
11
+ import com.facebook.react.bridge.BaseActivityEventListener
6
12
import com.facebook.react.bridge.ColorPropConverter
7
13
import com.facebook.react.bridge.ReactApplicationContext
8
14
import com.facebook.react.bridge.ReactContextBaseJavaModule
@@ -23,8 +29,16 @@ import com.luck.picture.lib.style.PictureSelectorStyle
23
29
import com.luck.picture.lib.style.PictureWindowAnimationStyle
24
30
import com.luck.picture.lib.style.SelectMainStyle
25
31
import com.luck.picture.lib.style.TitleBarStyle
32
+ import com.luck.picture.lib.utils.DateUtils
26
33
import com.luck.picture.lib.utils.DensityUtil
34
+ import com.yalantis.ucrop.UCrop
27
35
import com.yalantis.ucrop.UCrop.Options
36
+ import com.yalantis.ucrop.UCrop.REQUEST_CROP
37
+ import com.yalantis.ucrop.model.AspectRatio
38
+ import java.io.File
39
+ import java.net.HttpURLConnection
40
+ import java.net.URL
41
+
28
42
29
43
class MultipleImagePickerImp (reactContext : ReactApplicationContext ? ) :
30
44
ReactContextBaseJavaModule (reactContext), IApp {
@@ -65,7 +79,6 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
65
79
else -> SelectMimeType .ofAll()
66
80
}
67
81
68
-
69
82
val maxSelect = config.maxSelect?.toInt() ? : 20
70
83
val maxVideo = config.maxVideo?.toInt() ? : 20
71
84
val isPreview = config.isPreview ? : true
@@ -82,13 +95,10 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
82
95
83
96
val isCrop = config.crop != null
84
97
85
- PictureSelector .create(activity)
86
- .openGallery(chooseMode)
87
- .setImageEngine(imageEngine)
88
- .setSelectedData(dataList)
89
- .setSelectorUIStyle(style).apply {
98
+ PictureSelector .create(activity).openGallery(chooseMode).setImageEngine(imageEngine)
99
+ .setSelectedData(dataList).setSelectorUIStyle(style).apply {
90
100
if (isCrop) {
91
- setCropOption()
101
+ setCropOption(config.crop )
92
102
// Disabled force crop engine for multiple
93
103
if (! isMultiple) setCropEngine(CropEngine (cropOption))
94
104
else setEditMediaInterceptListener(setEditMediaEvent())
@@ -113,28 +123,18 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
113
123
if (videoQuality != null && videoQuality != 1.0 ) {
114
124
setVideoQuality(if (videoQuality > 0.5 ) 1 else 0 )
115
125
}
116
- }
117
- .setImageSpanCount(config.numberOfColumn?.toInt() ? : 3 )
118
- .setMaxSelectNum(maxSelect)
119
- .isDirectReturnSingle(true )
120
- .isSelectZoomAnim(true )
121
- .isPageStrategy(true , 50 )
126
+ }.setImageSpanCount(config.numberOfColumn?.toInt() ? : 3 ).setMaxSelectNum(maxSelect)
127
+ .isDirectReturnSingle(true ).isSelectZoomAnim(true ).isPageStrategy(true , 50 )
122
128
.isWithSelectVideoImage(true )
123
129
.setMaxVideoSelectNum(if (maxVideo != 20 ) maxVideo else maxSelect)
124
- .isMaxSelectEnabledMask(true )
125
- .isAutoVideoPlay(true )
126
- .isFastSlidingSelect(allowSwipeToSelect)
127
- .isPageSyncAlbumCount(true )
130
+ .isMaxSelectEnabledMask(true ).isAutoVideoPlay(true )
131
+ .isFastSlidingSelect(allowSwipeToSelect).isPageSyncAlbumCount(true )
128
132
// isPreview
129
- .isPreviewImage(isPreview)
130
- .isPreviewVideo(isPreview)
133
+ .isPreviewImage(isPreview).isPreviewVideo(isPreview)
131
134
//
132
- .isDisplayCamera(config.allowedCamera ? : true )
133
- .isDisplayTimeAxis(true )
134
- .setSelectionMode(selectMode)
135
- .isOriginalControl(config.isHiddenOriginalButton == false )
136
- .setLanguage(getLanguage())
137
- .isPreviewFullScreenMode(true )
135
+ .isDisplayCamera(config.allowedCamera ? : true ).isDisplayTimeAxis(true )
136
+ .setSelectionMode(selectMode).isOriginalControl(config.isHiddenOriginalButton == false )
137
+ .setLanguage(getLanguage()).isPreviewFullScreenMode(true )
138
138
.forResult(object : OnResultCallbackListener <LocalMedia ?> {
139
139
override fun onResult (localMedia : ArrayList <LocalMedia ?>? ) {
140
140
var data: Array <Result > = arrayOf()
@@ -161,6 +161,112 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
161
161
})
162
162
}
163
163
164
+ @ReactMethod
165
+ fun openCrop (
166
+ image : String ,
167
+ options : NitroCropConfig ,
168
+ resolved : (result: CropResult ) -> Unit ,
169
+ rejected : (reject: Double ) -> Unit
170
+ ) {
171
+
172
+
173
+ fun isImage (uri : Uri , contentResolver : ContentResolver ): Boolean {
174
+ val mimeType: String? = contentResolver.getType(uri)
175
+ return mimeType?.startsWith(" image/" ) == true
176
+ }
177
+
178
+ val uri = Uri .parse(image)
179
+ val isImageFile = isImage(uri, appContext.contentResolver)
180
+
181
+ if (! isImageFile) return rejected(0.0 )
182
+
183
+ cropOption = Options ()
184
+
185
+ setCropOption(
186
+ PickerCropConfig (
187
+ circle = options.circle,
188
+ ratio = options.ratio,
189
+ defaultRatio = options.defaultRatio,
190
+ freeStyle = options.freeStyle
191
+ )
192
+ )
193
+
194
+ try {
195
+ val uri = when {
196
+ // image network
197
+ image.startsWith(" http://" ) || image.startsWith(" https://" ) -> {
198
+ // Handle remote URL
199
+ val url = URL (image)
200
+ val connection = url.openConnection() as HttpURLConnection
201
+ connection.doInput = true
202
+ connection.connect()
203
+
204
+ val inputStream = connection.inputStream
205
+ // Create a temp file to store the image
206
+ val file = File (appContext.cacheDir, " CROP_" )
207
+ file.outputStream().use { output ->
208
+ inputStream.copyTo(output)
209
+ }
210
+
211
+ Uri .fromFile(file)
212
+ }
213
+
214
+
215
+ else -> {
216
+ Uri .parse(image)
217
+ }
218
+ }
219
+
220
+
221
+ val destinationUri = Uri .fromFile(
222
+ File (getSandboxPath(appContext), DateUtils .getCreateFileName(" CROP_" ) + " .jpeg" )
223
+ )
224
+
225
+ val uCrop = UCrop .of<Any >(uri, destinationUri).withOptions(cropOption)
226
+
227
+ // set engine
228
+ uCrop.setImageEngine(CropImageEngine ())
229
+ // start edit
230
+
231
+ val cropActivityEventListener = object : BaseActivityEventListener () {
232
+ override fun onActivityResult (
233
+ activity : Activity ,
234
+ requestCode : Int ,
235
+ resultCode : Int ,
236
+ data : Intent ?
237
+ ) {
238
+ if (resultCode == Activity .RESULT_OK && requestCode == REQUEST_CROP ) {
239
+ val resultUri = UCrop .getOutput(data!! )
240
+ val width = UCrop .getOutputImageWidth(data).toDouble()
241
+ val height = UCrop .getOutputImageHeight(data).toDouble()
242
+
243
+ resultUri?.let {
244
+ val result = CropResult (
245
+ path = it.toString(),
246
+ width,
247
+ height,
248
+ )
249
+ resolved(result)
250
+ }
251
+ } else if (resultCode == UCrop .RESULT_ERROR ) {
252
+ val cropError = UCrop .getError(data!! )
253
+ rejected(0.0 )
254
+ }
255
+
256
+ // Remove listener after getting result
257
+ reactApplicationContext.removeActivityEventListener(this )
258
+ }
259
+ }
260
+
261
+ // Add listener before starting UCrop
262
+ reactApplicationContext.addActivityEventListener(cropActivityEventListener)
263
+
264
+ currentActivity?.let { uCrop.start(it, REQUEST_CROP ) }
265
+ } catch (e: Exception ) {
266
+ rejected(0.0 )
267
+ }
268
+ }
269
+
164
270
private fun getLanguage (): Int {
165
271
return when (config.language) {
166
272
Language .VI -> LanguageConfig .VIETNAM // -> 🇻🇳 My country. Yeahhh
@@ -177,12 +283,10 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
177
283
}
178
284
}
179
285
180
- private fun setCropOption () {
181
- // val mainStyle: SelectMainStyle = style.selectMainStyle
182
-
286
+ private fun setCropOption (config : PickerCropConfig ? ) {
183
287
cropOption.setShowCropFrame(true )
184
288
cropOption.setShowCropGrid(true )
185
- cropOption.setCircleDimmedLayer(config.crop ?.circle ? : false )
289
+ cropOption.setCircleDimmedLayer(config?.circle ? : false )
186
290
cropOption.setCropOutputPathDir(getSandboxPath(appContext))
187
291
cropOption.isCropDragSmoothToCenter(true )
188
292
cropOption.isForbidSkipMultipleCrop(true )
@@ -191,8 +295,48 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
191
295
cropOption.setStatusBarColor(Color .WHITE )
192
296
cropOption.isDarkStatusBarBlack(true )
193
297
cropOption.isDragCropImages(true )
194
- cropOption.setFreeStyleCropEnabled(true )
298
+ cropOption.setFreeStyleCropEnabled(config?.freeStyle ? : true )
195
299
cropOption.setSkipCropMimeType(* getNotSupportCrop())
300
+
301
+
302
+ val ratioCount = config?.ratio?.size ? : 0
303
+
304
+ if (config?.defaultRatio != null || ratioCount > 0 ) {
305
+
306
+ var ratioList = arrayOf(AspectRatio (" Original" , 0f , 0f ))
307
+
308
+ if (ratioCount > 0 ) {
309
+ config?.ratio?.take(4 )?.toTypedArray()?.forEach { item ->
310
+ ratioList + = AspectRatio (
311
+ item.title, item.width.toFloat(), item.height.toFloat()
312
+ )
313
+ }
314
+ }
315
+
316
+ // Add default Aspects
317
+ ratioList + = arrayOf(
318
+ AspectRatio (null , 1f , 1f ),
319
+ AspectRatio (null , 16f , 9f ),
320
+ AspectRatio (null , 4f , 3f ),
321
+ AspectRatio (null , 3f , 2f )
322
+ )
323
+
324
+ config?.defaultRatio?.let {
325
+ val defaultRatio = AspectRatio (it.title, it.width.toFloat(), it.height.toFloat())
326
+ ratioList = arrayOf(defaultRatio) + ratioList
327
+
328
+ }
329
+
330
+ cropOption.apply {
331
+
332
+ setAspectRatioOptions(
333
+ 0 ,
334
+ * ratioList.take(5 ).toTypedArray()
335
+ )
336
+
337
+ }
338
+
339
+ }
196
340
}
197
341
198
342
0 commit comments