react-native-my-uploader-android 1.0.55 → 1.0.56
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.
|
@@ -44,6 +44,37 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
|
|
|
44
44
|
private const val E_INVALID_OPTIONS = "E_INVALID_OPTIONS"
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
private fun getWorkFile(rawUri: String): File {
|
|
48
|
+
val uri = Uri.parse(rawUri)
|
|
49
|
+
val scheme = uri.scheme?.lowercase() ?: ""
|
|
50
|
+
val tempFile = File.createTempFile("work_process_", ".tmp", reactContext.cacheDir)
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (scheme == "http" || scheme == "https") {
|
|
54
|
+
// Uzak URL'den indir
|
|
55
|
+
val url = java.net.URL(rawUri)
|
|
56
|
+
val connection = url.openConnection() as java.net.HttpURLConnection
|
|
57
|
+
connection.connectTimeout = 15000
|
|
58
|
+
connection.readTimeout = 20000
|
|
59
|
+
connection.inputStream.use { input ->
|
|
60
|
+
tempFile.outputStream().use { output -> input.copyTo(output) }
|
|
61
|
+
}
|
|
62
|
+
connection.disconnect()
|
|
63
|
+
} else {
|
|
64
|
+
// Yerel URI'den (content:// veya file://) kopyala
|
|
65
|
+
reactContext.contentResolver.openInputStream(uri).use { input ->
|
|
66
|
+
if (input == null) throw Exception("Dosya açılamadı: $rawUri")
|
|
67
|
+
tempFile.outputStream().use { output -> input.copyTo(output) }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (e: Exception) {
|
|
71
|
+
tempFile.delete()
|
|
72
|
+
throw e
|
|
73
|
+
}
|
|
74
|
+
return tempFile
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
47
78
|
@ReactMethod
|
|
48
79
|
fun openDocument(options: ReadableMap, promise: Promise) {
|
|
49
80
|
if (pickerPromise != null) {
|
|
@@ -212,90 +243,51 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
|
|
|
212
243
|
}
|
|
213
244
|
|
|
214
245
|
@ReactMethod
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
val maxDimension = 2048 // Uygulamanızın ihtiyacına göre ayarlayın
|
|
231
|
-
if (boundsOptions.outHeight > maxDimension || boundsOptions.outWidth > maxDimension) {
|
|
232
|
-
val halfHeight = boundsOptions.outHeight / 2
|
|
233
|
-
val halfWidth = boundsOptions.outWidth / 2
|
|
234
|
-
while ((halfHeight / inSampleSize) >= maxDimension && (halfWidth / inSampleSize) >= maxDimension) {
|
|
235
|
-
inSampleSize *= 2
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
decodeOptions.inSampleSize = inSampleSize
|
|
239
|
-
|
|
240
|
-
// Resmi optimize edilmiş boyutta belleğe yükle
|
|
241
|
-
val originalBitmap = contentResolver.openInputStream(sourceUri)?.use {
|
|
242
|
-
BitmapFactory.decodeStream(it, null, decodeOptions)
|
|
243
|
-
} ?: throw Exception("Bitmap oluşturulamadı (decodeStream).")
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
// 2. ADIM: Döndürme Matrisini Oluştur
|
|
247
|
-
// NOT: Bu fonksiyonda, JS'den gelen 'angle' değerini doğrudan kullanıyoruz.
|
|
248
|
-
// EXIF döndürmesi zaten orijinal resimde mevcuttur ve burada tekrar uygulanmamalıdır,
|
|
249
|
-
// çünkü amacımız kullanıcının isteği doğrultusunda ek bir döndürme yapmaktır.
|
|
250
|
-
val matrix = Matrix().apply {
|
|
251
|
-
postRotate(angle.toFloat())
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Döndürme işlemini uygula
|
|
255
|
-
val rotatedBitmap = Bitmap.createBitmap(
|
|
256
|
-
originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, true
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
// 3. ADIM: Döndürülmüş Bitmap'i geçici bir dosyaya kaydet
|
|
261
|
-
// (Bu kısım, cropImageWithUri'deki ile tamamen aynı)
|
|
262
|
-
val mimeType = contentResolver.getType(sourceUri) ?: "image/jpeg"
|
|
263
|
-
val fileExtension = when {
|
|
264
|
-
mimeType.contains("png") -> "png"
|
|
265
|
-
mimeType.contains("webp") -> "webp"
|
|
266
|
-
else -> "jpg"
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
val cacheDir = reactContext.cacheDir
|
|
270
|
-
val outputFile = createTempFile("rotated_", ".$fileExtension", cacheDir)
|
|
271
|
-
val fileOutputStream = outputFile.outputStream()
|
|
272
|
-
|
|
273
|
-
val compressFormat = when (fileExtension) {
|
|
274
|
-
"png" -> Bitmap.CompressFormat.PNG
|
|
275
|
-
"webp" -> if (android.os.Build.VERSION.SDK_INT >= 30) Bitmap.CompressFormat.WEBP_LOSSLESS else Bitmap.CompressFormat.WEBP
|
|
276
|
-
else -> Bitmap.CompressFormat.JPEG
|
|
277
|
-
}
|
|
246
|
+
fun RotateImageWithUri(fileUri: String, angle: Double, promise: Promise) {
|
|
247
|
+
Thread {
|
|
248
|
+
var workFile: File? = null
|
|
249
|
+
try {
|
|
250
|
+
// 1. Dosyayı yerel bir kopyaya al (İster URL olsun ister Local)
|
|
251
|
+
workFile = getWorkFile(fileUri)
|
|
252
|
+
|
|
253
|
+
// 2. Boyutları oku
|
|
254
|
+
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
|
255
|
+
workFile.inputStream().use { BitmapFactory.decodeStream(it, null, options) }
|
|
256
|
+
|
|
257
|
+
// 3. OOM önlemi
|
|
258
|
+
val decodeOptions = BitmapFactory.Options().apply {
|
|
259
|
+
inSampleSize = calculateInSampleSize(options, 2048, 2048)
|
|
260
|
+
}
|
|
278
261
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
262
|
+
// 4. Bitmap'i yükle
|
|
263
|
+
val originalBitmap = workFile.inputStream().use {
|
|
264
|
+
BitmapFactory.decodeStream(it, null, decodeOptions)
|
|
265
|
+
} ?: throw Exception("Bitmap decode edilemedi.")
|
|
282
266
|
|
|
267
|
+
// 5. Döndür
|
|
268
|
+
val matrix = Matrix().apply { postRotate(angle.toFloat()) }
|
|
269
|
+
val rotatedBitmap = Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, true)
|
|
283
270
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
271
|
+
// 6. Kaydet
|
|
272
|
+
val outputFile = File.createTempFile("rotated_", ".jpg", reactContext.cacheDir)
|
|
273
|
+
outputFile.outputStream().use {
|
|
274
|
+
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 95, it)
|
|
275
|
+
}
|
|
287
276
|
|
|
277
|
+
// Bellek temizliği
|
|
278
|
+
originalBitmap.recycle()
|
|
279
|
+
rotatedBitmap.recycle()
|
|
288
280
|
|
|
289
|
-
|
|
290
|
-
promise.resolve(Uri.fromFile(outputFile).toString())
|
|
281
|
+
promise.resolve(Uri.fromFile(outputFile).toString())
|
|
291
282
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
}
|
|
283
|
+
} catch (e: Exception) {
|
|
284
|
+
promise.reject("E_ROTATE_URI", e.message)
|
|
285
|
+
} finally {
|
|
286
|
+
// KRİTİK: İndirilen/kopyalanan geçici dosyayı SİL
|
|
287
|
+
workFile?.delete()
|
|
288
|
+
}
|
|
289
|
+
}.start()
|
|
290
|
+
}
|
|
299
291
|
|
|
300
292
|
|
|
301
293
|
@ReactMethod
|
|
@@ -393,139 +385,80 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
|
|
|
393
385
|
}
|
|
394
386
|
|
|
395
387
|
@ReactMethod
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
try {
|
|
403
|
-
val sourceUri = Uri.parse(fileUri)
|
|
404
|
-
val contentResolver = reactContext.contentResolver
|
|
405
|
-
|
|
406
|
-
// --- 1. İYİLEŞTİRME: EXIF Döndürme Bilgisini Oku ---
|
|
407
|
-
var orientation = 0
|
|
408
|
-
try {
|
|
409
|
-
// InputStream'i EXIF okumak için bir kere açıyoruz
|
|
410
|
-
contentResolver.openInputStream(sourceUri)?.use { inputStream ->
|
|
411
|
-
val exif = ExifInterface(inputStream)
|
|
412
|
-
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
|
413
|
-
}
|
|
414
|
-
} catch (e: Exception) {
|
|
415
|
-
// EXIF okunamıyorsa (örn: PNG), görmezden gel.
|
|
416
|
-
println("EXIF bilgisi okunamadı: ${e.message}")
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// --- 2. İYİLEŞTİRME: OOM Riskini Azaltmak için BitmapFactory.Options ---
|
|
420
|
-
// Önce, sadece resmin boyutlarını almak için `inJustDecodeBounds` kullanıyoruz.
|
|
421
|
-
// Bu, resmi belleğe yüklemeden boyutlarını okumamızı sağlar.
|
|
422
|
-
val boundsOptions = BitmapFactory.Options()
|
|
423
|
-
boundsOptions.inJustDecodeBounds = true
|
|
424
|
-
contentResolver.openInputStream(sourceUri)?.use { inputStream ->
|
|
425
|
-
BitmapFactory.decodeStream(inputStream, null, boundsOptions)
|
|
426
|
-
}
|
|
388
|
+
fun CropImageWithUri(fileUri: String, crop: ReadableMap, promise: Promise) {
|
|
389
|
+
Thread {
|
|
390
|
+
var workFile: File? = null
|
|
391
|
+
try {
|
|
392
|
+
// 1. Dosyayı yerele al
|
|
393
|
+
workFile = getWorkFile(fileUri)
|
|
427
394
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
var inSampleSize = 1
|
|
435
|
-
val maxDimension = 2048 // Uygulamanızın ihtiyacına göre bu değeri ayarlayın
|
|
436
|
-
if (originalHeight > maxDimension || originalWidth > maxDimension) {
|
|
437
|
-
val halfHeight: Int = originalHeight / 2
|
|
438
|
-
val halfWidth: Int = originalWidth / 2
|
|
439
|
-
while (halfHeight / inSampleSize >= maxDimension && halfWidth / inSampleSize >= maxDimension) {
|
|
440
|
-
inSampleSize *= 2
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
decodeOptions.inSampleSize = inSampleSize
|
|
444
|
-
|
|
445
|
-
// Şimdi resmi, hesaplanan `inSampleSize` ile belleğe yüklüyoruz.
|
|
446
|
-
val originalBitmap = contentResolver.openInputStream(sourceUri)?.use { inputStream ->
|
|
447
|
-
BitmapFactory.decodeStream(inputStream, null, decodeOptions)
|
|
448
|
-
} ?: throw Exception("Bitmap oluşturulamadı (decodeStream).")
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
// --- 3. İYİLEŞTİRME: Döndürme Matrisini Oluştur ---
|
|
452
|
-
val matrix = Matrix()
|
|
453
|
-
// EXIF bilgisine göre döndürme matrisini ayarla
|
|
454
|
-
when (orientation) {
|
|
455
|
-
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
|
|
456
|
-
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
|
|
457
|
-
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
|
|
458
|
-
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.preScale(-1.0f, 1.0f)
|
|
459
|
-
ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.preScale(1.0f, -1.0f)
|
|
460
|
-
// Diğer karmaşık oryantasyonlar da eklenebilir...
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// Döndürme işlemini uygula
|
|
464
|
-
val rotatedBitmap =
|
|
465
|
-
if (!matrix.isIdentity) {
|
|
466
|
-
Bitmap.createBitmap(
|
|
467
|
-
originalBitmap,0, 0, originalBitmap.width, originalBitmap.height, matrix, true
|
|
468
|
-
).also {
|
|
469
|
-
originalBitmap.recycle()
|
|
470
|
-
}
|
|
471
|
-
} else {
|
|
472
|
-
originalBitmap
|
|
473
|
-
}
|
|
395
|
+
// 2. EXIF bilgisini yerel dosyadan oku (URL'de bu çalışmazdı, şimdi çalışır)
|
|
396
|
+
val orientation = try {
|
|
397
|
+
ExifInterface(workFile.absolutePath).getAttributeInt(
|
|
398
|
+
ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL
|
|
399
|
+
)
|
|
400
|
+
} catch (_: Exception) { ExifInterface.ORIENTATION_NORMAL }
|
|
474
401
|
|
|
402
|
+
// 3. Boyut ölçümü
|
|
403
|
+
val boundsOptions = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
|
404
|
+
workFile.inputStream().use { BitmapFactory.decodeStream(it, null, boundsOptions) }
|
|
475
405
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
val rw = crop.getDouble("width")
|
|
480
|
-
val rh = crop.getDouble("height")
|
|
481
|
-
|
|
482
|
-
// NOT: Artık rotatedBitmap'in boyutlarını kullanıyoruz!
|
|
483
|
-
val px = (rx * rotatedBitmap.width).toInt().coerceIn(0, rotatedBitmap.width - 1)
|
|
484
|
-
val py = (ry * rotatedBitmap.height).toInt().coerceIn(0, rotatedBitmap.height - 1)
|
|
485
|
-
val pw = (rw * rotatedBitmap.width).toInt().coerceAtMost(rotatedBitmap.width - px).coerceAtLeast(1)
|
|
486
|
-
val ph = (rh * rotatedBitmap.height).toInt().coerceAtMost(rotatedBitmap.height - py).coerceAtLeast(1)
|
|
406
|
+
val decodeOptions = BitmapFactory.Options().apply {
|
|
407
|
+
inSampleSize = calculateInSampleSize(boundsOptions, 2048, 2048)
|
|
408
|
+
}
|
|
487
409
|
|
|
410
|
+
// 4. Bitmap yükle
|
|
411
|
+
val originalBitmap = workFile.inputStream().use {
|
|
412
|
+
BitmapFactory.decodeStream(it, null, decodeOptions)
|
|
413
|
+
} ?: throw Exception("Bitmap yüklenemedi.")
|
|
414
|
+
|
|
415
|
+
// 5. EXIF Rotasyonu uygula
|
|
416
|
+
val matrix = Matrix()
|
|
417
|
+
when (orientation) {
|
|
418
|
+
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
|
|
419
|
+
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
|
|
420
|
+
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
|
|
421
|
+
}
|
|
488
422
|
|
|
489
|
-
|
|
490
|
-
|
|
423
|
+
val rotatedBitmap = if (!matrix.isIdentity) {
|
|
424
|
+
Bitmap.createBitmap(originalBitmap, 0, 0, originalBitmap.width, originalBitmap.height, matrix, true)
|
|
425
|
+
.also { originalBitmap.recycle() }
|
|
426
|
+
} else originalBitmap
|
|
491
427
|
|
|
428
|
+
// 6. Kırpma koordinatları
|
|
429
|
+
val rx = crop.getDouble("x")
|
|
430
|
+
val ry = crop.getDouble("y")
|
|
431
|
+
val rw = crop.getDouble("width")
|
|
432
|
+
val rh = crop.getDouble("height")
|
|
492
433
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
mimeType.contains("png") -> "png"
|
|
498
|
-
mimeType.contains("webp") -> "webp"
|
|
499
|
-
else -> "jpg"
|
|
500
|
-
}
|
|
501
|
-
val cacheDir = reactContext.cacheDir
|
|
502
|
-
val outputFile = createTempFile("cropped_", ".$fileExtension", cacheDir)
|
|
503
|
-
val fileOutputStream = outputFile.outputStream()
|
|
504
|
-
val compressFormat = when (fileExtension) {
|
|
505
|
-
"png" -> Bitmap.CompressFormat.PNG
|
|
506
|
-
"webp" -> if (android.os.Build.VERSION.SDK_INT >= 30) Bitmap.CompressFormat.WEBP_LOSSLESS else Bitmap.CompressFormat.WEBP
|
|
507
|
-
else -> Bitmap.CompressFormat.JPEG
|
|
508
|
-
}
|
|
509
|
-
fileOutputStream.use { stream ->
|
|
510
|
-
croppedBitmap.compress(compressFormat, 95, stream)
|
|
511
|
-
}
|
|
434
|
+
val px = (rx * rotatedBitmap.width).toInt().coerceIn(0, rotatedBitmap.width - 1)
|
|
435
|
+
val py = (ry * rotatedBitmap.height).toInt().coerceIn(0, rotatedBitmap.height - 1)
|
|
436
|
+
val pw = (rw * rotatedBitmap.width).toInt().coerceAtMost(rotatedBitmap.width - px).coerceAtLeast(1)
|
|
437
|
+
val ph = (rh * rotatedBitmap.height).toInt().coerceAtMost(rotatedBitmap.height - py).coerceAtLeast(1)
|
|
512
438
|
|
|
439
|
+
val croppedBitmap = Bitmap.createBitmap(rotatedBitmap, px, py, pw, ph)
|
|
513
440
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
441
|
+
// 7. Çıktıyı kaydet
|
|
442
|
+
val outputFile = File.createTempFile("cropped_", ".jpg", reactContext.cacheDir)
|
|
443
|
+
outputFile.outputStream().use {
|
|
444
|
+
croppedBitmap.compress(Bitmap.CompressFormat.JPEG, 95, it)
|
|
445
|
+
}
|
|
517
446
|
|
|
447
|
+
// Temizlik
|
|
448
|
+
if (originalBitmap != rotatedBitmap) originalBitmap.recycle()
|
|
449
|
+
rotatedBitmap.recycle()
|
|
450
|
+
croppedBitmap.recycle()
|
|
518
451
|
|
|
519
|
-
|
|
520
|
-
promise.resolve(Uri.fromFile(outputFile).toString())
|
|
452
|
+
promise.resolve(Uri.fromFile(outputFile).toString())
|
|
521
453
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
}
|
|
454
|
+
} catch (e: Exception) {
|
|
455
|
+
promise.reject("E_CROP_URI", e.message)
|
|
456
|
+
} finally {
|
|
457
|
+
// KRİTİK: Geçici dosyayı SİL
|
|
458
|
+
workFile?.delete()
|
|
459
|
+
}
|
|
460
|
+
}.start()
|
|
461
|
+
}
|
|
529
462
|
|
|
530
463
|
@ReactMethod
|
|
531
464
|
fun DeleteTempFiles(fileUris: ReadableArray, promise: Promise) {
|