react-native-my-uploader-android 1.0.27 → 1.0.29
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.
- package/android/src/main/java/com/myuploaderandroid/DownloadFileModule.kt +91 -58
- package/android/src/main/java/com/myuploaderandroid/MyUploaderModule.kt +71 -87
- package/android/src/main/java/com/myuploaderandroid/MyUploaderPackage.kt +1 -1
- package/lib/commonjs/NativeModules.js +16 -0
- package/lib/commonjs/NativeModules.js.map +1 -0
- package/lib/commonjs/components/DownloadFile.js +37 -49
- package/lib/commonjs/components/DownloadFile.js.map +1 -1
- package/lib/commonjs/components/MyUploader.js +54 -80
- package/lib/commonjs/components/MyUploader.js.map +1 -1
- package/lib/commonjs/index.d.js.map +1 -1
- package/lib/commonjs/index.js +4 -4
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/NativeModules.js +10 -0
- package/lib/module/NativeModules.js.map +1 -0
- package/lib/module/components/DownloadFile.js +38 -50
- package/lib/module/components/DownloadFile.js.map +1 -1
- package/lib/module/components/MyUploader.js +52 -79
- package/lib/module/components/MyUploader.js.map +1 -1
- package/lib/module/index.d.js +6 -0
- package/lib/module/index.d.js.map +1 -1
- package/lib/module/index.js +7 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/package.json +1 -1
- package/src/NativeModules.ts +9 -0
- package/src/components/DownloadFile.tsx +38 -62
- package/src/components/MyUploader.tsx +59 -90
- package/src/index.d.ts +13 -4
- package/src/index.ts +11 -6
- package/src/types.ts +96 -41
- package/android/src/main/java/com/myuploader/MyUploaderModule.kt +0 -173
- package/android/src/main/java/com/myuploader/MyUploaderPackage.kt +0 -16
- package/lib/commonjs/NativeMyUploader.js +0 -15
- package/lib/commonjs/NativeMyUploader.js.map +0 -1
- package/lib/module/NativeMyUploader.js +0 -9
- package/lib/module/NativeMyUploader.js.map +0 -1
- package/src/NativeMyUploader.ts +0 -25
|
@@ -12,54 +12,70 @@ import android.util.Log
|
|
|
12
12
|
import android.webkit.CookieManager
|
|
13
13
|
import android.webkit.URLUtil
|
|
14
14
|
import com.facebook.react.bridge.*
|
|
15
|
-
import
|
|
15
|
+
import java.io.File
|
|
16
16
|
import java.net.HttpURLConnection
|
|
17
17
|
import java.net.URL
|
|
18
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
18
19
|
|
|
19
20
|
class DownloadFileModule(private val reactContext: ReactApplicationContext) :
|
|
20
21
|
ReactContextBaseJavaModule(reactContext) {
|
|
21
22
|
|
|
22
23
|
private val TAG = "DownloadFileModule"
|
|
23
|
-
private val downloadPromises =
|
|
24
|
+
private val downloadPromises = ConcurrentHashMap<Long, Promise>()
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
|
|
31
|
-
if (downloadPromises.containsKey(downloadId)) {
|
|
32
|
-
val promise = downloadPromises[downloadId]
|
|
26
|
+
private val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
|
27
|
+
override fun onReceive(context: Context, intent: Intent) {
|
|
28
|
+
val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
|
|
29
|
+
if (downloadId != -1L) {
|
|
30
|
+
downloadPromises.remove(downloadId)?.let { promise ->
|
|
33
31
|
checkDownloadStatus(downloadId, promise)
|
|
34
|
-
downloadPromises.remove(downloadId)
|
|
35
32
|
}
|
|
36
33
|
}
|
|
37
34
|
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override fun getName(): String = "DownloadFile"
|
|
38
|
+
|
|
39
|
+
init {
|
|
38
40
|
val intentFilter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
|
39
41
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
40
|
-
|
|
42
|
+
reactContext.registerReceiver(receiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
41
43
|
} else {
|
|
42
|
-
|
|
44
|
+
reactContext.registerReceiver(receiver, intentFilter)
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
override fun onCatalystInstanceDestroy() {
|
|
49
|
+
try {
|
|
50
|
+
reactContext.unregisterReceiver(receiver)
|
|
51
|
+
} catch (e: Exception) {
|
|
52
|
+
Log.w(TAG, "Receiver unregister warning: ${e.message}")
|
|
53
|
+
}
|
|
54
|
+
super.onCatalystInstanceDestroy()
|
|
55
|
+
}
|
|
56
|
+
|
|
46
57
|
@ReactMethod
|
|
47
58
|
fun downloadFile(fileUrl: String, options: ReadableMap, promise: Promise) {
|
|
48
59
|
val isDebug = if (options.hasKey("debug")) options.getBoolean("debug") else false
|
|
49
|
-
val shouldLog = BuildConfig.DEBUG && isDebug
|
|
50
60
|
val maxSizeMB = if (options.hasKey("maxSize")) options.getDouble("maxSize") else 0.0
|
|
61
|
+
|
|
62
|
+
// Allowed File Types (Mime Types)
|
|
63
|
+
val allowedTypes = if (options.hasKey("fileTypes")) {
|
|
64
|
+
options.getArray("fileTypes")?.toArrayList()?.mapNotNull { it.toString() } ?: emptyList()
|
|
65
|
+
} else emptyList()
|
|
51
66
|
|
|
52
|
-
if (
|
|
67
|
+
if (isDebug) Log.d(TAG, "İndirme başlatılıyor: $fileUrl")
|
|
53
68
|
|
|
54
69
|
Thread {
|
|
70
|
+
var connection: HttpURLConnection? = null
|
|
55
71
|
try {
|
|
56
72
|
val url = URL(fileUrl)
|
|
57
|
-
|
|
73
|
+
connection = url.openConnection() as HttpURLConnection
|
|
58
74
|
val cookie = CookieManager.getInstance().getCookie(fileUrl)
|
|
59
75
|
if (cookie != null) connection.setRequestProperty("Cookie", cookie)
|
|
60
|
-
connection.requestMethod = "HEAD"
|
|
76
|
+
connection.requestMethod = "HEAD" // Sadece başlıkları çek
|
|
61
77
|
connection.connect()
|
|
62
|
-
|
|
78
|
+
|
|
63
79
|
val responseCode = connection.responseCode
|
|
64
80
|
if (responseCode !in 200..299) {
|
|
65
81
|
promise.reject("E_HTTP_ERROR", "Sunucu hatası: $responseCode")
|
|
@@ -68,22 +84,49 @@ class DownloadFileModule(private val reactContext: ReactApplicationContext) :
|
|
|
68
84
|
|
|
69
85
|
val fileSize = connection.contentLengthLong
|
|
70
86
|
val contentType = connection.contentType
|
|
71
|
-
val contentDisposition = connection.getHeaderField("Content-Disposition")
|
|
72
87
|
val cleanContentType = contentType?.split(";")?.get(0)?.trim() ?: "*/*"
|
|
88
|
+
|
|
89
|
+
// 1. Content-Type Kontrolü
|
|
90
|
+
if (allowedTypes.isNotEmpty() && !allowedTypes.contains("*/*")) {
|
|
91
|
+
var isAllowed = false
|
|
92
|
+
for (type in allowedTypes) {
|
|
93
|
+
if (type == "*/*" || cleanContentType.equals(type, ignoreCase = true) || cleanContentType.startsWith(type.replace("*", ""))) {
|
|
94
|
+
isAllowed = true
|
|
95
|
+
break
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!isAllowed) {
|
|
99
|
+
promise.reject("E_INVALID_FILE_TYPE", "Dosya türü ($cleanContentType) izin verilenler listesinde yok.")
|
|
100
|
+
return@Thread
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 2. Boyut Kontrolü
|
|
105
|
+
if (maxSizeMB > 0 && fileSize > 0 && fileSize > maxSizeMB * 1024 * 1024) {
|
|
106
|
+
promise.reject("E_FILE_TOO_LARGE", "Dosya boyutu (${(fileSize / 1024.0 / 1024.0).toInt()}MB) limitin (${maxSizeMB}MB) üzerinde.")
|
|
107
|
+
return@Thread
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
val contentDisposition = connection.getHeaderField("Content-Disposition")
|
|
73
111
|
val fileName = URLUtil.guessFileName(fileUrl, contentDisposition, cleanContentType)
|
|
74
112
|
|
|
75
|
-
|
|
113
|
+
// Dosya Adı Çakışma Önleme
|
|
114
|
+
val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
|
115
|
+
val baseName = fileName.substringBeforeLast(".")
|
|
116
|
+
val ext = fileName.substringAfterLast(".", "")
|
|
117
|
+
var counter = 1
|
|
118
|
+
var finalFileName = fileName
|
|
76
119
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
120
|
+
while (File(downloadsDir, finalFileName).exists()) {
|
|
121
|
+
finalFileName = if (ext.isNotEmpty()) "$baseName($counter).$ext" else "$baseName($counter)"
|
|
122
|
+
counter++
|
|
80
123
|
}
|
|
81
124
|
|
|
82
125
|
val request = DownloadManager.Request(Uri.parse(fileUrl))
|
|
83
|
-
.setTitle(
|
|
84
|
-
.setDescription("
|
|
126
|
+
.setTitle(finalFileName)
|
|
127
|
+
.setDescription("Dosya indiriliyor...")
|
|
85
128
|
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
|
86
|
-
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,
|
|
129
|
+
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, finalFileName)
|
|
87
130
|
.setAllowedOverMetered(true)
|
|
88
131
|
|
|
89
132
|
if (cookie != null) request.addRequestHeader("Cookie", cookie)
|
|
@@ -91,13 +134,13 @@ class DownloadFileModule(private val reactContext: ReactApplicationContext) :
|
|
|
91
134
|
|
|
92
135
|
val downloadManager = reactContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
|
93
136
|
val downloadId = downloadManager.enqueue(request)
|
|
94
|
-
|
|
95
|
-
if (BuildConfig.DEBUG) Log.i(TAG, "İndirme sıraya eklendi. ID: #$downloadId")
|
|
137
|
+
|
|
96
138
|
downloadPromises[downloadId] = promise
|
|
97
139
|
|
|
98
140
|
} catch (e: Exception) {
|
|
99
|
-
if (BuildConfig.DEBUG) Log.e(TAG, "İndirme başlatılırken hata!", e)
|
|
100
141
|
promise.reject("E_DOWNLOAD_SETUP_FAILED", e.message)
|
|
142
|
+
} finally {
|
|
143
|
+
connection?.disconnect()
|
|
101
144
|
}
|
|
102
145
|
}.start()
|
|
103
146
|
}
|
|
@@ -107,38 +150,28 @@ class DownloadFileModule(private val reactContext: ReactApplicationContext) :
|
|
|
107
150
|
val query = DownloadManager.Query().setFilterById(id)
|
|
108
151
|
val cursor = downloadManager.query(query)
|
|
109
152
|
|
|
110
|
-
if (cursor
|
|
111
|
-
|
|
112
|
-
|
|
153
|
+
if (cursor == null) {
|
|
154
|
+
promise?.reject("E_DB_CURSOR_NULL", "Download cursor null")
|
|
155
|
+
return
|
|
156
|
+
}
|
|
113
157
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
val
|
|
117
|
-
|
|
158
|
+
cursor.use {
|
|
159
|
+
if (it.moveToFirst()) {
|
|
160
|
+
val statusIndex = it.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
|
161
|
+
val status = if (statusIndex >= 0) it.getInt(statusIndex) else DownloadManager.STATUS_FAILED
|
|
162
|
+
|
|
163
|
+
if (status == DownloadManager.STATUS_SUCCESSFUL) {
|
|
164
|
+
val uriIndex = it.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
|
|
165
|
+
val localUri = if (uriIndex >= 0) it.getString(uriIndex) else null
|
|
166
|
+
promise?.resolve(localUri)
|
|
167
|
+
} else {
|
|
168
|
+
val reasonIndex = it.getColumnIndex(DownloadManager.COLUMN_REASON)
|
|
169
|
+
val reason = if (reasonIndex >= 0) it.getInt(reasonIndex) else -1
|
|
170
|
+
promise?.reject("E_DOWNLOAD_FAILED", "İndirme başarısız kod: $reason")
|
|
171
|
+
}
|
|
118
172
|
} else {
|
|
119
|
-
|
|
120
|
-
val reason = cursor.getInt(reasonIndex)
|
|
121
|
-
val reasonText = getDownloadErrorReason(reason)
|
|
122
|
-
promise?.reject("E_DOWNLOAD_FAILED", "İndirme başarısız. Sebep: $reasonText (Kod: $reason)")
|
|
173
|
+
promise?.reject("E_DOWNLOAD_NOT_FOUND", "İndirme kaydı bulunamadı.")
|
|
123
174
|
}
|
|
124
|
-
} else {
|
|
125
|
-
promise?.reject("E_DOWNLOAD_NOT_FOUND", "İndirme işlemi bulunamadı.")
|
|
126
|
-
}
|
|
127
|
-
cursor.close()
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private fun getDownloadErrorReason(reasonCode: Int): String {
|
|
131
|
-
return when (reasonCode) {
|
|
132
|
-
DownloadManager.ERROR_CANNOT_RESUME -> "İndirme devam ettirilemiyor."
|
|
133
|
-
DownloadManager.ERROR_DEVICE_NOT_FOUND -> "Harici depolama bulunamadı."
|
|
134
|
-
DownloadManager.ERROR_FILE_ALREADY_EXISTS -> "Aynı isimde bir dosya zaten var."
|
|
135
|
-
DownloadManager.ERROR_FILE_ERROR -> "Dosya sistemi hatası."
|
|
136
|
-
DownloadManager.ERROR_HTTP_DATA_ERROR -> "Sunucu ile veri alışverişinde hata."
|
|
137
|
-
DownloadManager.ERROR_INSUFFICIENT_SPACE -> "Cihazda yeterli alan yok."
|
|
138
|
-
DownloadManager.ERROR_TOO_MANY_REDIRECTS -> "Çok fazla yönlendirme yapıldı."
|
|
139
|
-
DownloadManager.ERROR_UNHANDLED_HTTP_CODE -> "Sunucudan beklenmeyen HTTP kodu alındı."
|
|
140
|
-
DownloadManager.ERROR_UNKNOWN -> "Bilinmeyen bir hata oluştu."
|
|
141
|
-
else -> "Bilinmeyen hata."
|
|
142
175
|
}
|
|
143
176
|
}
|
|
144
177
|
}
|
|
@@ -14,7 +14,7 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
|
|
|
14
14
|
|
|
15
15
|
private var pickerPromise: Promise? = null
|
|
16
16
|
private var maxSize: Double = 0.0
|
|
17
|
-
private var maxFiles: Int =
|
|
17
|
+
private var maxFiles: Int = 1
|
|
18
18
|
private var excludedUris: List<String> = emptyList()
|
|
19
19
|
|
|
20
20
|
init {
|
|
@@ -25,149 +25,133 @@ class MyUploaderModule(private val reactContext: ReactApplicationContext) :
|
|
|
25
25
|
|
|
26
26
|
companion object {
|
|
27
27
|
private const val REQUEST_CODE = 9900
|
|
28
|
-
private const val E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST"
|
|
29
|
-
private const val E_PICKER_CANCELLED = "E_PICKER_CANCELLED"
|
|
30
|
-
private const val E_FAILED_TO_OPEN_DOCUMENT = "E_FAILED_TO_OPEN_DOCUMENT"
|
|
31
|
-
private const val E_FILE_TOO_LARGE = "E_FILE_TOO_LARGE"
|
|
32
|
-
private const val E_MAX_FILES_EXCEEDED = "E_MAX_FILES_EXCEEDED"
|
|
33
|
-
private const val E_INVALID_OPTIONS = "E_INVALID_OPTIONS"
|
|
34
28
|
}
|
|
35
29
|
|
|
36
30
|
@ReactMethod
|
|
37
31
|
fun openDocument(options: ReadableMap, promise: Promise) {
|
|
38
32
|
if (pickerPromise != null) {
|
|
39
|
-
promise.reject(
|
|
33
|
+
promise.reject("E_BUSY", "Zaten açık bir işlem var.")
|
|
40
34
|
return
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
this.pickerPromise = promise
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
val
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
multipleFiles
|
|
57
|
-
|
|
58
|
-
else -> jsMaxFiles // Belirtilen değeri kullan
|
|
39
|
+
// JS'den gelen opsiyonları al
|
|
40
|
+
val multipleFiles = if(options.hasKey("multipleFiles")) options.getBoolean("multipleFiles") else false
|
|
41
|
+
val jsMaxFiles = if(options.hasKey("maxFiles")) options.getInt("maxFiles") else 0
|
|
42
|
+
this.maxSize = if(options.hasKey("maxSize")) options.getDouble("maxSize") else 0.0
|
|
43
|
+
this.excludedUris = if(options.hasKey("excludedUris"))
|
|
44
|
+
options.getArray("excludedUris")?.toArrayList()?.mapNotNull { it.toString() } ?: emptyList()
|
|
45
|
+
else emptyList()
|
|
46
|
+
|
|
47
|
+
// maxFiles mantığı
|
|
48
|
+
this.maxFiles = when {
|
|
49
|
+
multipleFiles && jsMaxFiles == 0 -> 99 // Limit yoksa varsayılan
|
|
50
|
+
!multipleFiles -> 1
|
|
51
|
+
else -> jsMaxFiles
|
|
59
52
|
}
|
|
60
53
|
|
|
61
|
-
|
|
62
|
-
this.maxFiles = effectiveMaxFiles
|
|
63
|
-
this.maxSize = options.takeIf { it.hasKey("maxSize") }?.getDouble("maxSize") ?: 0.0
|
|
64
|
-
this.excludedUris = options.takeIf { it.hasKey("excludedUris") }
|
|
65
|
-
?.getArray("excludedUris")?.toArrayList()?.mapNotNull { it.toString() } ?: emptyList()
|
|
66
|
-
|
|
67
|
-
val currentActivity = reactContext.currentActivity
|
|
54
|
+
val currentActivity = currentActivity
|
|
68
55
|
if (currentActivity == null) {
|
|
69
|
-
|
|
56
|
+
promise.reject("E_NO_ACTIVITY", "Activity bulunamadı.")
|
|
70
57
|
pickerPromise = null
|
|
71
58
|
return
|
|
72
59
|
}
|
|
73
60
|
|
|
74
|
-
val fileTypes = options.
|
|
75
|
-
|
|
76
|
-
|
|
61
|
+
val fileTypes = if (options.hasKey("fileTypes"))
|
|
62
|
+
options.getArray("fileTypes")?.toArrayList()?.mapNotNull { it.toString() } ?: listOf("*/*")
|
|
63
|
+
else listOf("*/*")
|
|
64
|
+
|
|
65
|
+
val mimeTypes = fileTypes.toTypedArray()
|
|
77
66
|
|
|
78
67
|
try {
|
|
79
|
-
// ANAHTAR: ACTION_GET_CONTENT, güvenilir çoklu seçimi etkinleştirir.
|
|
80
68
|
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
|
81
|
-
type = "*/*"
|
|
69
|
+
type = "*/*" // Filter by MIME types in extra
|
|
82
70
|
addCategory(Intent.CATEGORY_OPENABLE)
|
|
83
71
|
putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
|
|
84
|
-
|
|
85
|
-
if (multipleFiles) {
|
|
86
|
-
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
87
|
-
}
|
|
72
|
+
if (multipleFiles) putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
|
88
73
|
}
|
|
89
|
-
currentActivity.startActivityForResult(Intent.createChooser(intent, "Dosya
|
|
74
|
+
currentActivity.startActivityForResult(Intent.createChooser(intent, "Dosya Seç"), REQUEST_CODE)
|
|
90
75
|
} catch (e: Exception) {
|
|
91
|
-
pickerPromise?.reject(
|
|
76
|
+
pickerPromise?.reject("E_OPEN_FAILED", e)
|
|
92
77
|
pickerPromise = null
|
|
93
78
|
}
|
|
94
79
|
}
|
|
95
80
|
|
|
96
81
|
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
|
|
97
|
-
if (requestCode != REQUEST_CODE || pickerPromise == null)
|
|
82
|
+
if (requestCode != REQUEST_CODE || pickerPromise == null) return
|
|
98
83
|
|
|
99
|
-
if (resultCode == Activity.RESULT_OK
|
|
100
|
-
val
|
|
101
|
-
|
|
84
|
+
if (resultCode == Activity.RESULT_OK) {
|
|
85
|
+
val uris = mutableListOf<Uri>()
|
|
86
|
+
|
|
87
|
+
if (data?.clipData != null) {
|
|
88
|
+
val clip = data.clipData!!
|
|
102
89
|
for (i in 0 until clip.itemCount) {
|
|
103
|
-
|
|
90
|
+
uris.add(clip.getItemAt(i).uri)
|
|
104
91
|
}
|
|
105
|
-
}
|
|
106
|
-
|
|
92
|
+
} else if (data?.data != null) {
|
|
93
|
+
uris.add(data.data!!)
|
|
107
94
|
}
|
|
108
|
-
|
|
109
|
-
val newSelectedUris = allSelectedUris.filter { uri -> !excludedUris.contains(uri.toString()) }
|
|
110
95
|
|
|
111
|
-
|
|
112
|
-
|
|
96
|
+
// Excluded URI Filtreleme
|
|
97
|
+
val filteredUris = uris.filter { !excludedUris.contains(it.toString()) }
|
|
98
|
+
|
|
99
|
+
if (filteredUris.size > maxFiles) {
|
|
100
|
+
pickerPromise?.reject("E_MAX_FILES", "Maksimum $maxFiles dosya seçebilirsiniz.")
|
|
113
101
|
pickerPromise = null
|
|
114
102
|
return
|
|
115
103
|
}
|
|
116
104
|
|
|
117
105
|
try {
|
|
118
|
-
val
|
|
119
|
-
pickerPromise?.resolve(
|
|
106
|
+
val result = processSelectedFiles(filteredUris)
|
|
107
|
+
pickerPromise?.resolve(result)
|
|
120
108
|
} catch (e: Exception) {
|
|
121
|
-
pickerPromise?.reject(
|
|
109
|
+
pickerPromise?.reject("E_PROCESS_FAILED", e.message)
|
|
122
110
|
}
|
|
123
111
|
} else {
|
|
124
|
-
pickerPromise?.reject(
|
|
112
|
+
pickerPromise?.reject("E_CANCELLED", "İptal edildi.")
|
|
125
113
|
}
|
|
126
114
|
pickerPromise = null
|
|
127
115
|
}
|
|
128
116
|
|
|
129
|
-
override fun onNewIntent(intent: Intent) {}
|
|
117
|
+
override fun onNewIntent(intent: Intent?) {}
|
|
130
118
|
|
|
131
119
|
private fun processSelectedFiles(uris: List<Uri>): WritableArray {
|
|
132
|
-
val
|
|
120
|
+
val resultArr = WritableNativeArray()
|
|
133
121
|
val maxSizeBytes = (maxSize * 1024 * 1024).toLong()
|
|
134
122
|
|
|
135
123
|
for (uri in uris) {
|
|
136
124
|
reactContext.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
|
|
137
125
|
if (cursor.moveToFirst()) {
|
|
138
|
-
val
|
|
139
|
-
val
|
|
140
|
-
|
|
141
|
-
val
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
126
|
+
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
|
127
|
+
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
|
128
|
+
|
|
129
|
+
val size = if(sizeIndex >= 0) cursor.getLong(sizeIndex) else 0L
|
|
130
|
+
val name = if(nameIndex >= 0) cursor.getString(nameIndex) else "unknown"
|
|
131
|
+
|
|
132
|
+
if (maxSizeBytes > 0 && size > maxSizeBytes) {
|
|
133
|
+
throw Exception("Dosya ($name) boyutu çok büyük. Limit: ${maxSize}MB")
|
|
145
134
|
}
|
|
146
135
|
|
|
147
|
-
|
|
148
|
-
val base64 = reactContext.contentResolver.openInputStream(uri)?.use {
|
|
149
|
-
val
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
putString("base64", base64)
|
|
164
|
-
}
|
|
165
|
-
fileObjects.pushMap(fileMap)
|
|
136
|
+
// Base64 okuma
|
|
137
|
+
val base64 = reactContext.contentResolver.openInputStream(uri)?.use { stream ->
|
|
138
|
+
val bytes = stream.readBytes()
|
|
139
|
+
Base64.encodeToString(bytes, Base64.NO_WRAP)
|
|
140
|
+
} ?: ""
|
|
141
|
+
|
|
142
|
+
val mimeType = reactContext.contentResolver.getType(uri) ?: "application/octet-stream"
|
|
143
|
+
|
|
144
|
+
val map = WritableNativeMap()
|
|
145
|
+
map.putString("uri", uri.toString())
|
|
146
|
+
map.putString("name", name)
|
|
147
|
+
map.putString("type", mimeType)
|
|
148
|
+
map.putDouble("size", size.toDouble())
|
|
149
|
+
map.putString("base64", base64)
|
|
150
|
+
|
|
151
|
+
resultArr.pushMap(map)
|
|
166
152
|
}
|
|
167
153
|
}
|
|
168
154
|
}
|
|
169
|
-
return
|
|
155
|
+
return resultArr
|
|
170
156
|
}
|
|
171
|
-
|
|
172
|
-
private fun getMimeType(fileName: String): String? = URLConnection.guessContentTypeFromName(fileName)
|
|
173
157
|
}
|
|
@@ -8,7 +8,7 @@ import com.facebook.react.uimanager.ViewManager
|
|
|
8
8
|
class MyUploaderPackage : ReactPackage {
|
|
9
9
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
10
10
|
return listOf(
|
|
11
|
-
MyUploaderModule(reactContext)
|
|
11
|
+
MyUploaderModule(reactContext),
|
|
12
12
|
DownloadFileModule(reactContext)
|
|
13
13
|
)
|
|
14
14
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NativePicker = exports.NativeDownload = void 0;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
const {
|
|
9
|
+
DownloadFile,
|
|
10
|
+
DocumentPicker
|
|
11
|
+
} = _reactNative.NativeModules;
|
|
12
|
+
if (!DownloadFile) console.warn("MyUploader: DownloadFile native module not found.");
|
|
13
|
+
if (!DocumentPicker) console.warn("MyUploader: DocumentPicker native module not found.");
|
|
14
|
+
const NativeDownload = exports.NativeDownload = DownloadFile;
|
|
15
|
+
const NativePicker = exports.NativePicker = DocumentPicker;
|
|
16
|
+
//# sourceMappingURL=NativeModules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_reactNative","require","DownloadFile","DocumentPicker","NativeModules","console","warn","NativeDownload","exports","NativePicker"],"sources":["NativeModules.ts"],"sourcesContent":["import { NativeModules } from 'react-native';\r\n\r\nconst { DownloadFile, DocumentPicker } = NativeModules;\r\n\r\nif (!DownloadFile) console.warn(\"MyUploader: DownloadFile native module not found.\");\r\nif (!DocumentPicker) console.warn(\"MyUploader: DocumentPicker native module not found.\");\r\n\r\nexport const NativeDownload = DownloadFile;\r\nexport const NativePicker = DocumentPicker;"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAM;EAAEC,YAAY;EAAEC;AAAe,CAAC,GAAGC,0BAAa;AAEtD,IAAI,CAACF,YAAY,EAAEG,OAAO,CAACC,IAAI,CAAC,mDAAmD,CAAC;AACpF,IAAI,CAACH,cAAc,EAAEE,OAAO,CAACC,IAAI,CAAC,qDAAqD,CAAC;AAEjF,MAAMC,cAAc,GAAAC,OAAA,CAAAD,cAAA,GAAGL,YAAY;AACnC,MAAMO,YAAY,GAAAD,OAAA,CAAAC,YAAA,GAAGN,cAAc","ignoreList":[]}
|
|
@@ -6,13 +6,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
+
var _NativeModules = require("../NativeModules");
|
|
9
10
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
10
|
-
const {
|
|
11
|
-
DownloadFile: DownloadFileModule
|
|
12
|
-
} = _reactNative.NativeModules;
|
|
13
11
|
const DownloadFile = ({
|
|
14
12
|
files,
|
|
15
|
-
|
|
13
|
+
multipleDownload = false,
|
|
16
14
|
disabled = false,
|
|
17
15
|
debug = false,
|
|
18
16
|
maxSize = 0,
|
|
@@ -21,82 +19,72 @@ const DownloadFile = ({
|
|
|
21
19
|
buttonIcon,
|
|
22
20
|
ButtonStyle,
|
|
23
21
|
ButtonTextStyle,
|
|
24
|
-
onSuccess
|
|
25
|
-
onError
|
|
22
|
+
onSuccess,
|
|
23
|
+
onError
|
|
26
24
|
}) => {
|
|
27
25
|
const [isLoading, setIsLoading] = (0, _react.useState)(false);
|
|
28
26
|
const handlePress = async () => {
|
|
29
|
-
if (!
|
|
30
|
-
onError(new Error("DownloadFile native module is not available."));
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
27
|
+
if (disabled || isLoading || !files.length) return;
|
|
33
28
|
setIsLoading(true);
|
|
34
|
-
const
|
|
29
|
+
const options = {
|
|
30
|
+
debug,
|
|
35
31
|
maxSize,
|
|
36
|
-
fileTypes
|
|
37
|
-
debug
|
|
32
|
+
fileTypes
|
|
38
33
|
};
|
|
34
|
+
const targetFiles = multipleDownload ? files : [files[0]];
|
|
39
35
|
try {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const finalResult = {
|
|
36
|
+
const promises = targetFiles.map(url => _NativeModules.NativeDownload.downloadFile(url, options));
|
|
37
|
+
const results = await Promise.allSettled(promises);
|
|
38
|
+
const final = {
|
|
44
39
|
successful: [],
|
|
45
40
|
skipped: []
|
|
46
41
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
finalResult.successful.push({
|
|
42
|
+
results.forEach((res, index) => {
|
|
43
|
+
var _targetFiles$index;
|
|
44
|
+
const originalUrl = (_targetFiles$index = targetFiles[index]) !== null && _targetFiles$index !== void 0 ? _targetFiles$index : "";
|
|
45
|
+
if (res.status === 'fulfilled') {
|
|
46
|
+
final.successful.push({
|
|
53
47
|
originalUrl,
|
|
54
|
-
|
|
55
|
-
localUri: result.value
|
|
48
|
+
localUri: res.value
|
|
56
49
|
});
|
|
57
50
|
} else {
|
|
58
|
-
|
|
51
|
+
var _res$reason;
|
|
52
|
+
final.skipped.push({
|
|
59
53
|
originalUrl,
|
|
60
|
-
|
|
61
|
-
reason: result.reason.message
|
|
54
|
+
reason: ((_res$reason = res.reason) === null || _res$reason === void 0 ? void 0 : _res$reason.message) || "Bilinmeyen Hata"
|
|
62
55
|
});
|
|
63
56
|
}
|
|
64
57
|
});
|
|
65
|
-
onSuccess(
|
|
66
|
-
} catch (
|
|
67
|
-
onError(error);
|
|
58
|
+
if (onSuccess) onSuccess(final);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
if (onError) onError(e);else console.error(e);
|
|
68
61
|
} finally {
|
|
69
62
|
setIsLoading(false);
|
|
70
63
|
}
|
|
71
64
|
};
|
|
72
|
-
const content = isLoading ? /*#__PURE__*/_react.default.createElement(_reactNative.ActivityIndicator, {
|
|
73
|
-
color: "#FFFFFF"
|
|
74
|
-
}) : buttonIcon || /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
75
|
-
style: [styles.buttonText, ButtonTextStyle]
|
|
76
|
-
}, buttonPlaceHolder);
|
|
77
65
|
return /*#__PURE__*/_react.default.createElement(_reactNative.TouchableOpacity, {
|
|
78
|
-
style: [styles.button, ButtonStyle, (disabled || isLoading) && styles.
|
|
66
|
+
style: [styles.button, ButtonStyle, (disabled || isLoading) && styles.disabled],
|
|
79
67
|
onPress: handlePress,
|
|
80
68
|
disabled: disabled || isLoading
|
|
81
|
-
},
|
|
69
|
+
}, isLoading ? /*#__PURE__*/_react.default.createElement(_reactNative.ActivityIndicator, {
|
|
70
|
+
color: "#FFF"
|
|
71
|
+
}) : buttonIcon ? buttonIcon : /*#__PURE__*/_react.default.createElement(_reactNative.Text, {
|
|
72
|
+
style: [styles.text, ButtonTextStyle]
|
|
73
|
+
}, buttonPlaceHolder));
|
|
82
74
|
};
|
|
83
75
|
const styles = _reactNative.StyleSheet.create({
|
|
84
76
|
button: {
|
|
85
|
-
backgroundColor: '#
|
|
86
|
-
|
|
87
|
-
paddingVertical: 10,
|
|
77
|
+
backgroundColor: '#03DAC6',
|
|
78
|
+
padding: 12,
|
|
88
79
|
borderRadius: 8,
|
|
89
|
-
alignItems: 'center'
|
|
90
|
-
justifyContent: 'center',
|
|
91
|
-
flexDirection: 'row'
|
|
80
|
+
alignItems: 'center'
|
|
92
81
|
},
|
|
93
|
-
|
|
94
|
-
color: '#
|
|
95
|
-
fontSize: 16,
|
|
82
|
+
text: {
|
|
83
|
+
color: '#000',
|
|
96
84
|
fontWeight: 'bold'
|
|
97
85
|
},
|
|
98
|
-
|
|
99
|
-
backgroundColor: '#
|
|
86
|
+
disabled: {
|
|
87
|
+
backgroundColor: '#AAA'
|
|
100
88
|
}
|
|
101
89
|
});
|
|
102
90
|
var _default = exports.default = DownloadFile;
|