11package com.margelo.nitro.multipleimagepicker
22
3+ import android.app.Activity
4+ import android.content.ContentResolver
35import android.content.Context
6+ import android.content.Intent
47import android.graphics.Color
8+ import android.net.Uri
59import androidx.core.content.ContextCompat
10+ import com.facebook.react.bridge.ActivityEventListener
11+ import com.facebook.react.bridge.BaseActivityEventListener
612import com.facebook.react.bridge.ColorPropConverter
713import com.facebook.react.bridge.ReactApplicationContext
814import com.facebook.react.bridge.ReactContextBaseJavaModule
@@ -23,8 +29,16 @@ import com.luck.picture.lib.style.PictureSelectorStyle
2329import com.luck.picture.lib.style.PictureWindowAnimationStyle
2430import com.luck.picture.lib.style.SelectMainStyle
2531import com.luck.picture.lib.style.TitleBarStyle
32+ import com.luck.picture.lib.utils.DateUtils
2633import com.luck.picture.lib.utils.DensityUtil
34+ import com.yalantis.ucrop.UCrop
2735import 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+
2842
2943class MultipleImagePickerImp (reactContext : ReactApplicationContext ? ) :
3044 ReactContextBaseJavaModule (reactContext), IApp {
@@ -65,7 +79,6 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
6579 else -> SelectMimeType .ofAll()
6680 }
6781
68-
6982 val maxSelect = config.maxSelect?.toInt() ? : 20
7083 val maxVideo = config.maxVideo?.toInt() ? : 20
7184 val isPreview = config.isPreview ? : true
@@ -82,13 +95,10 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
8295
8396 val isCrop = config.crop != null
8497
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 {
90100 if (isCrop) {
91- setCropOption()
101+ setCropOption(config.crop )
92102 // Disabled force crop engine for multiple
93103 if (! isMultiple) setCropEngine(CropEngine (cropOption))
94104 else setEditMediaInterceptListener(setEditMediaEvent())
@@ -113,28 +123,18 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
113123 if (videoQuality != null && videoQuality != 1.0 ) {
114124 setVideoQuality(if (videoQuality > 0.5 ) 1 else 0 )
115125 }
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 )
122128 .isWithSelectVideoImage(true )
123129 .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 )
128132 // isPreview
129- .isPreviewImage(isPreview)
130- .isPreviewVideo(isPreview)
133+ .isPreviewImage(isPreview).isPreviewVideo(isPreview)
131134 //
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 )
138138 .forResult(object : OnResultCallbackListener <LocalMedia ?> {
139139 override fun onResult (localMedia : ArrayList <LocalMedia ?>? ) {
140140 var data: Array <Result > = arrayOf()
@@ -161,6 +161,112 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
161161 })
162162 }
163163
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+
164270 private fun getLanguage (): Int {
165271 return when (config.language) {
166272 Language .VI -> LanguageConfig .VIETNAM // -> 🇻🇳 My country. Yeahhh
@@ -177,12 +283,10 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
177283 }
178284 }
179285
180- private fun setCropOption () {
181- // val mainStyle: SelectMainStyle = style.selectMainStyle
182-
286+ private fun setCropOption (config : PickerCropConfig ? ) {
183287 cropOption.setShowCropFrame(true )
184288 cropOption.setShowCropGrid(true )
185- cropOption.setCircleDimmedLayer(config.crop ?.circle ? : false )
289+ cropOption.setCircleDimmedLayer(config?.circle ? : false )
186290 cropOption.setCropOutputPathDir(getSandboxPath(appContext))
187291 cropOption.isCropDragSmoothToCenter(true )
188292 cropOption.isForbidSkipMultipleCrop(true )
@@ -191,8 +295,48 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
191295 cropOption.setStatusBarColor(Color .WHITE )
192296 cropOption.isDarkStatusBarBlack(true )
193297 cropOption.isDragCropImages(true )
194- cropOption.setFreeStyleCropEnabled(true )
298+ cropOption.setFreeStyleCropEnabled(config?.freeStyle ? : true )
195299 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+ }
196340 }
197341
198342
0 commit comments