@ammarahmed/react-native-upload 6.16.0 → 6.17.0
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/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/appfolio/extensions/ContextExtensions.kt +63 -0
- package/android/src/main/java/com/appfolio/extensions/UploadExtensions.kt +57 -0
- package/android/src/main/java/com/appfolio/uploader/GlobalRequestObserverDelegate.kt +62 -0
- package/android/src/main/java/com/appfolio/uploader/ModifiedBinaryUploadRequest.kt +29 -0
- package/android/src/main/java/com/appfolio/uploader/ModifiedHttpUploadRequest.kt +57 -0
- package/android/src/main/java/com/appfolio/uploader/ModifiedMultipartUploadRequest.kt +60 -0
- package/android/src/main/java/com/appfolio/uploader/UploaderModule.kt +384 -0
- package/android/src/main/java/com/appfolio/uploader/UploaderReactPackage.java +32 -0
- package/android/src/main/java/com/appfolio/work/TaskCompletionNotifier.kt +47 -0
- package/android/src/main/java/com/appfolio/work/UploadManager.kt +109 -0
- package/android/src/main/java/com/appfolio/work/UploadWorker.kt +20 -0
- package/package.json +1 -1
- package/src/index.ts +549 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<manifest />
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
package com.appfolio.extensions
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import com.appfolio.work.TaskCompletionNotifier
|
|
5
|
+
import com.appfolio.work.UploadManager
|
|
6
|
+
import net.gotev.uploadservice.UploadTask
|
|
7
|
+
import net.gotev.uploadservice.data.UploadNotificationConfig
|
|
8
|
+
import net.gotev.uploadservice.data.UploadTaskParameters
|
|
9
|
+
import net.gotev.uploadservice.logger.UploadServiceLogger
|
|
10
|
+
import net.gotev.uploadservice.observer.task.BroadcastEmitter
|
|
11
|
+
|
|
12
|
+
data class UploadTaskCreationParameters(
|
|
13
|
+
val params: UploadTaskParameters,
|
|
14
|
+
val notificationConfig: UploadNotificationConfig
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new task instance based on the requested task class in the intent.
|
|
19
|
+
* @param creationParameters task creation params
|
|
20
|
+
* @return task instance or null if the task class is not supported or invalid
|
|
21
|
+
*/
|
|
22
|
+
@Suppress("UNCHECKED_CAST")
|
|
23
|
+
fun Context.getUploadTask(
|
|
24
|
+
creationParameters: UploadTaskCreationParameters,
|
|
25
|
+
notificationId: Int
|
|
26
|
+
): UploadTask? {
|
|
27
|
+
return try {
|
|
28
|
+
val observers = arrayOf(
|
|
29
|
+
BroadcastEmitter(this),
|
|
30
|
+
TaskCompletionNotifier()
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
val taskClass = Class.forName(creationParameters.params.taskClass) as Class<out UploadTask>
|
|
34
|
+
val uploadTask = taskClass.newInstance().apply {
|
|
35
|
+
init(
|
|
36
|
+
context = this@getUploadTask,
|
|
37
|
+
taskParams = creationParameters.params,
|
|
38
|
+
notificationConfig = creationParameters.notificationConfig,
|
|
39
|
+
notificationId = notificationId,
|
|
40
|
+
taskObservers = observers
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
UploadServiceLogger.debug(
|
|
45
|
+
component = UploadManager.TAG,
|
|
46
|
+
uploadId = creationParameters.params.id,
|
|
47
|
+
message = {
|
|
48
|
+
"Successfully created new task with class: ${taskClass.name}"
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
uploadTask
|
|
52
|
+
} catch (exc: Throwable) {
|
|
53
|
+
UploadServiceLogger.error(
|
|
54
|
+
component = UploadManager.TAG,
|
|
55
|
+
uploadId = "",
|
|
56
|
+
exception = exc,
|
|
57
|
+
message = {
|
|
58
|
+
"Error while instantiating new task"
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
null
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package com.appfolio.extensions
|
|
2
|
+
|
|
3
|
+
import androidx.work.Constraints
|
|
4
|
+
import androidx.work.Data
|
|
5
|
+
import androidx.work.NetworkType
|
|
6
|
+
import androidx.work.OneTimeWorkRequest
|
|
7
|
+
import com.google.gson.Gson
|
|
8
|
+
import net.gotev.uploadservice.data.UploadFile
|
|
9
|
+
import net.gotev.uploadservice.data.UploadNotificationConfig
|
|
10
|
+
import net.gotev.uploadservice.data.UploadTaskParameters
|
|
11
|
+
import net.gotev.uploadservice.extensions.setOrRemove
|
|
12
|
+
import net.gotev.uploadservice.persistence.PersistableData
|
|
13
|
+
|
|
14
|
+
private const val PROPERTY_PARAM_NAME = "multipartParamName"
|
|
15
|
+
private const val PROPERTY_REMOTE_FILE_NAME = "multipartRemoteFileName"
|
|
16
|
+
private const val PROPERTY_CONTENT_TYPE = "multipartContentType"
|
|
17
|
+
const val PARAM_KEY_TASK_PARAMS = "PARAM_KEY_TASK_PARAMS"
|
|
18
|
+
const val PARAM_KEY_NOTIF_CONFIG = "PARAM_KEY_NOTIF_CONFIG"
|
|
19
|
+
|
|
20
|
+
internal var UploadFile.parameterName: String?
|
|
21
|
+
get() = properties[PROPERTY_PARAM_NAME]
|
|
22
|
+
set(value) {
|
|
23
|
+
properties.setOrRemove(PROPERTY_PARAM_NAME, value)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
internal var UploadFile.remoteFileName: String?
|
|
27
|
+
get() = properties[PROPERTY_REMOTE_FILE_NAME]
|
|
28
|
+
set(value) {
|
|
29
|
+
properties.setOrRemove(PROPERTY_REMOTE_FILE_NAME, value)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
internal var UploadFile.contentType: String?
|
|
33
|
+
get() = properties[PROPERTY_CONTENT_TYPE]
|
|
34
|
+
set(value) {
|
|
35
|
+
properties.setOrRemove(PROPERTY_CONTENT_TYPE, value)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
internal fun UploadTaskParameters.toJson(): String = toPersistableData().toJson()
|
|
39
|
+
|
|
40
|
+
internal fun String.toUploadTaskParameters(): UploadTaskParameters = UploadTaskParameters.createFromPersistableData(PersistableData.fromJson(this))
|
|
41
|
+
|
|
42
|
+
internal fun UploadNotificationConfig.toJson(): String = Gson().toJson(this)
|
|
43
|
+
|
|
44
|
+
internal fun String.toUploadNotificationConfig() = Gson().fromJson(this, UploadNotificationConfig::class.java)
|
|
45
|
+
|
|
46
|
+
internal fun OneTimeWorkRequest.Builder.setData(uploadTaskParameters: UploadTaskParameters, notificationConfig: UploadNotificationConfig) {
|
|
47
|
+
val data = Data.Builder()
|
|
48
|
+
data.putString(PARAM_KEY_TASK_PARAMS, uploadTaskParameters.toJson())
|
|
49
|
+
data.putString(PARAM_KEY_NOTIF_CONFIG, notificationConfig.toJson())
|
|
50
|
+
setInputData(data.build())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
internal fun OneTimeWorkRequest.Builder.shouldLimitNetwork(limit: Boolean) {
|
|
54
|
+
val network = if (limit) NetworkType.UNMETERED else NetworkType.CONNECTED
|
|
55
|
+
val constraints = Constraints.Builder().setRequiredNetworkType(network).build()
|
|
56
|
+
setConstraints(constraints)
|
|
57
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package com.appfolio.uploader
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.react.bridge.Arguments
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.bridge.WritableMap
|
|
8
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
|
9
|
+
import net.gotev.uploadservice.data.UploadInfo
|
|
10
|
+
import net.gotev.uploadservice.network.ServerResponse
|
|
11
|
+
import net.gotev.uploadservice.observer.request.RequestObserverDelegate
|
|
12
|
+
|
|
13
|
+
class GlobalRequestObserverDelegate(reactContext: ReactApplicationContext) : RequestObserverDelegate {
|
|
14
|
+
private val TAG = "UploadReceiver"
|
|
15
|
+
|
|
16
|
+
private var reactContext: ReactApplicationContext = reactContext
|
|
17
|
+
|
|
18
|
+
override fun onCompleted(context: Context, uploadInfo: UploadInfo) {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override fun onCompletedWhileNotObserving() {
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override fun onError(context: Context, uploadInfo: UploadInfo, exception: Throwable) {
|
|
25
|
+
|
|
26
|
+
val params = Arguments.createMap()
|
|
27
|
+
params.putString("id", uploadInfo.uploadId)
|
|
28
|
+
|
|
29
|
+
// Make sure we do not try to call getMessage() on a null object
|
|
30
|
+
if (exception != null) {
|
|
31
|
+
params.putString("error", exception.message)
|
|
32
|
+
} else {
|
|
33
|
+
params.putString("error", "Unknown exception")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
sendEvent("error", params, context)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override fun onProgress(context: Context, uploadInfo: UploadInfo) {
|
|
40
|
+
val params = Arguments.createMap()
|
|
41
|
+
params.putString("id", uploadInfo.uploadId)
|
|
42
|
+
params.putInt("progress", uploadInfo.progressPercent) //0-100
|
|
43
|
+
|
|
44
|
+
sendEvent("progress", params, context)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
override fun onSuccess(context: Context, uploadInfo: UploadInfo, serverResponse: ServerResponse) {
|
|
48
|
+
val params = Arguments.createMap()
|
|
49
|
+
params.putString("id", uploadInfo.uploadId)
|
|
50
|
+
params.putInt("responseCode", serverResponse.code)
|
|
51
|
+
params.putString("responseBody", serverResponse.bodyString)
|
|
52
|
+
sendEvent("completed", params, context)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Sends an event to the JS module.
|
|
57
|
+
*/
|
|
58
|
+
private fun sendEvent(eventName: String, params: WritableMap?, context: Context) {
|
|
59
|
+
reactContext?.getJSModule(RCTDeviceEventEmitter::class.java)?.emit("RNFileUploader-$eventName", params)
|
|
60
|
+
?: Log.e(TAG, "sendEvent() failed due reactContext == null!")
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package com.appfolio.uploader
|
|
2
|
+
import android.content.Context
|
|
3
|
+
import net.gotev.uploadservice.UploadTask
|
|
4
|
+
import net.gotev.uploadservice.data.UploadFile
|
|
5
|
+
import net.gotev.uploadservice.protocols.binary.BinaryUploadRequest
|
|
6
|
+
import net.gotev.uploadservice.protocols.binary.BinaryUploadTask
|
|
7
|
+
import java.io.FileNotFoundException
|
|
8
|
+
import java.io.IOException
|
|
9
|
+
|
|
10
|
+
class ModifiedBinaryUploadRequest(context: Context, serverUrl: String, limitNetwork: Boolean) :
|
|
11
|
+
ModifiedHttpUploadRequest<ModifiedBinaryUploadRequest>(context, serverUrl, limitNetwork) {
|
|
12
|
+
|
|
13
|
+
override val taskClass: Class<out UploadTask>
|
|
14
|
+
get() = BinaryUploadTask::class.java
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sets the file used as raw body of the upload request.
|
|
18
|
+
*
|
|
19
|
+
* @param path path to the file that you want to upload
|
|
20
|
+
* @throws FileNotFoundException if the file to upload does not exist
|
|
21
|
+
* @return [BinaryUploadRequest]
|
|
22
|
+
*/
|
|
23
|
+
@Throws(IOException::class)
|
|
24
|
+
fun setFileToUpload(path: String): ModifiedBinaryUploadRequest {
|
|
25
|
+
files.clear()
|
|
26
|
+
files.add(UploadFile(path))
|
|
27
|
+
return this
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package com.appfolio.uploader
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.work.OneTimeWorkRequest
|
|
5
|
+
import androidx.work.WorkManager
|
|
6
|
+
import com.appfolio.extensions.setData
|
|
7
|
+
import com.appfolio.extensions.shouldLimitNetwork
|
|
8
|
+
import com.appfolio.work.UploadManager
|
|
9
|
+
import com.appfolio.work.UploadWorker
|
|
10
|
+
import net.gotev.uploadservice.HttpUploadRequest
|
|
11
|
+
import net.gotev.uploadservice.data.UploadTaskParameters
|
|
12
|
+
import java.util.*
|
|
13
|
+
|
|
14
|
+
abstract class ModifiedHttpUploadRequest<B : HttpUploadRequest<B>>(context: Context, serverUrl: String, private val limitNetwork: Boolean = false) :
|
|
15
|
+
HttpUploadRequest<B>(context, serverUrl) {
|
|
16
|
+
|
|
17
|
+
private var started: Boolean = false
|
|
18
|
+
private var uploadId = UUID.randomUUID().toString()
|
|
19
|
+
private val uploadTaskParameters: UploadTaskParameters
|
|
20
|
+
get() = UploadTaskParameters(
|
|
21
|
+
taskClass = taskClass.name,
|
|
22
|
+
id = uploadId,
|
|
23
|
+
serverUrl = serverUrl,
|
|
24
|
+
maxRetries = maxRetries,
|
|
25
|
+
autoDeleteSuccessfullyUploadedFiles = autoDeleteSuccessfullyUploadedFiles,
|
|
26
|
+
files = files,
|
|
27
|
+
additionalParameters = getAdditionalParameters()
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
override fun startUpload(): String {
|
|
31
|
+
require(files.isNotEmpty()) { "Set the file to be used in the request body first!" }
|
|
32
|
+
check(!started) {
|
|
33
|
+
"You have already called startUpload() on this Upload request instance once and you " +
|
|
34
|
+
"cannot call it multiple times. Check your code."
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
check(!UploadManager.taskList.contains(uploadTaskParameters.id)) {
|
|
38
|
+
"You have tried to perform startUpload() using the same uploadID of an " +
|
|
39
|
+
"already running task. You're trying to use the same ID for multiple uploads."
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
started = true
|
|
43
|
+
val workManager: WorkManager = WorkManager.getInstance(context)
|
|
44
|
+
val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
|
|
45
|
+
uploadRequest.shouldLimitNetwork(limitNetwork)
|
|
46
|
+
uploadRequest.addTag("${UploadWorker::class.java.simpleName}-$uploadId")
|
|
47
|
+
uploadRequest.setData(uploadTaskParameters, notificationConfig(context, uploadId))
|
|
48
|
+
workManager.enqueue(uploadRequest.build())
|
|
49
|
+
|
|
50
|
+
return uploadTaskParameters.id;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fun setCustomUploadID(uploadID: String) {
|
|
54
|
+
this.uploadId = uploadID
|
|
55
|
+
setUploadID(uploadID)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package com.appfolio.uploader
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import com.appfolio.extensions.contentType
|
|
5
|
+
import com.appfolio.extensions.parameterName
|
|
6
|
+
import com.appfolio.extensions.remoteFileName
|
|
7
|
+
import net.gotev.uploadservice.UploadTask
|
|
8
|
+
import net.gotev.uploadservice.data.UploadFile
|
|
9
|
+
import net.gotev.uploadservice.protocols.multipart.MultipartUploadTask
|
|
10
|
+
import java.io.FileNotFoundException
|
|
11
|
+
|
|
12
|
+
class ModifiedMultipartUploadRequest(context: Context, serverUrl: String, limitNetwork: Boolean) :
|
|
13
|
+
ModifiedHttpUploadRequest<ModifiedMultipartUploadRequest>(context, serverUrl, limitNetwork) {
|
|
14
|
+
|
|
15
|
+
override val taskClass: Class<out UploadTask>
|
|
16
|
+
get() = MultipartUploadTask::class.java
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Adds a file to this upload request.
|
|
20
|
+
*
|
|
21
|
+
* @param filePath path to the file that you want to upload
|
|
22
|
+
* @param parameterName Name of the form parameter that will contain file's data
|
|
23
|
+
* @param fileName File name seen by the server side script. If null, the original file name
|
|
24
|
+
* will be used
|
|
25
|
+
* @param contentType Content type of the file. If null or empty, the mime type will be
|
|
26
|
+
* automatically detected. If fore some reasons autodetection fails,
|
|
27
|
+
* `application/octet-stream` will be used by default
|
|
28
|
+
* @return [ModifiedMultipartUploadRequest]
|
|
29
|
+
*/
|
|
30
|
+
@Throws(FileNotFoundException::class)
|
|
31
|
+
@JvmOverloads
|
|
32
|
+
fun addFileToUpload(
|
|
33
|
+
filePath: String,
|
|
34
|
+
parameterName: String,
|
|
35
|
+
fileName: String? = null,
|
|
36
|
+
contentType: String? = null
|
|
37
|
+
): ModifiedMultipartUploadRequest {
|
|
38
|
+
require(filePath.isNotBlank() && parameterName.isNotBlank()) {
|
|
39
|
+
"Please specify valid filePath and parameterName. They cannot be blank."
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
files.add(UploadFile(filePath).apply {
|
|
43
|
+
this.parameterName = parameterName
|
|
44
|
+
|
|
45
|
+
this.contentType = if (contentType.isNullOrBlank()) {
|
|
46
|
+
handler.contentType(context)
|
|
47
|
+
} else {
|
|
48
|
+
contentType
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
remoteFileName = if (fileName.isNullOrBlank()) {
|
|
52
|
+
handler.name(context)
|
|
53
|
+
} else {
|
|
54
|
+
fileName
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return this
|
|
59
|
+
}
|
|
60
|
+
}
|