From c09344fb2b612f5ab10b3edb6998142c2dd18250 Mon Sep 17 00:00:00 2001 From: Martin Donadieu Date: Tue, 3 Sep 2024 18:24:01 +0100 Subject: [PATCH] WIP Android --- README.md | 10 ++ android/build.gradle | 1 + android/src/main/AndroidManifest.xml | 33 +++++- .../ee/forgr/capacitor/uploader/Uploader.java | 112 +++++++++++++++++- .../capacitor/uploader/UploaderPlugin.java | 75 +++++++++++- 5 files changed, 221 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 159af52..d9f115b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,16 @@ npm install @capgo/capacitor-uploader npx cap sync ``` +## Android: + +Add the following to your `AndroidManifest.xml` file: + +```xml + + + +``` + ## API diff --git a/android/build.gradle b/android/build.gradle index 74efcb2..56b2ddd 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -52,6 +52,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':capacitor-android') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation 'net.gotev:uploadservice:4.7.0' testImplementation "junit:junit:$junitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index a2f47b6..1eb51ff 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,2 +1,33 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/android/src/main/java/ee/forgr/capacitor/uploader/Uploader.java b/android/src/main/java/ee/forgr/capacitor/uploader/Uploader.java index 87953e4..cb1f9c7 100644 --- a/android/src/main/java/ee/forgr/capacitor/uploader/Uploader.java +++ b/android/src/main/java/ee/forgr/capacitor/uploader/Uploader.java @@ -1,11 +1,115 @@ package ee.forgr.capacitor.uploader; -import android.util.Log; +import android.app.Application; +import android.content.Context; +import android.net.Uri; +import android.provider.OpenableColumns; +import android.database.Cursor; + +import net.gotev.uploadservice.UploadServiceConfig; +import net.gotev.uploadservice.data.UploadNotificationConfig; +import net.gotev.uploadservice.data.UploadNotificationStatusConfig; +import net.gotev.uploadservice.protocols.multipart.MultipartUploadRequest; +import net.gotev.uploadservice.observer.request.RequestObserverDelegate; + +import java.util.Map; public class Uploader { - public String echo(String value) { - Log.i("Echo", value); - return value; + private final Context context; + private final RequestObserverDelegate delegate; + + public Uploader(Context context, RequestObserverDelegate delegate) { + this.context = context; + this.delegate = delegate; + initializeUploadService(context); + } + + private void initializeUploadService(Context context) { + Application application = getApplication(context); + if (application != null) { + UploadServiceConfig.initialize(application, "ee.forgr.capacitor.uploader.notification_channel_id", true); + } else { + throw new IllegalStateException("Unable to get Application instance"); + } + } + + private Application getApplication(Context context) { + if (context == null) { + return null; + } else if (context instanceof Application) { + return (Application) context; + } else { + return getApplication(context.getApplicationContext()); + } + } + + public String startUpload(String filePath, String serverUrl, Map headers, String notificationTitle) throws Exception { + UploadNotificationStatusConfig progress = new UploadNotificationStatusConfig(notificationTitle, notificationTitle + " - In Progress"); + UploadNotificationStatusConfig success = new UploadNotificationStatusConfig(notificationTitle, notificationTitle + " - Completed"); + UploadNotificationStatusConfig error = new UploadNotificationStatusConfig(notificationTitle, notificationTitle + " - Error"); + UploadNotificationStatusConfig cancelled = new UploadNotificationStatusConfig(notificationTitle, notificationTitle + " - Cancelled"); + + UploadNotificationConfig notificationConfig = new UploadNotificationConfig( + "ee.forgr.capacitor.uploader.notification_channel_id", + false, + progress, + success, + error, + cancelled + ); + + MultipartUploadRequest request = new MultipartUploadRequest(context, serverUrl) + .setMethod("POST") + .addFileToUpload(filePath, "file") + .setNotificationConfig((ctx, uploadId) -> notificationConfig); + + // Add headers + for (Map.Entry entry : headers.entrySet()) { + request.addHeader(entry.getKey(), entry.getValue()); + } + + // Set max retries + request.setMaxRetries(2); + + // Set file name if it's a content URI + if (filePath.startsWith("content://")) { + Uri uri = Uri.parse(filePath); + String fileName = getFileNameFromUri(uri); + if (fileName != null) { + request.addParameter("filename", fileName); + } + } + + // Start the upload + return request.startUpload(); + } + + public void removeUpload(String uploadId) { + net.gotev.uploadservice.UploadService.stopUpload(uploadId); + } + + private String getFileNameFromUri(Uri uri) { + String result = null; + if (uri.getScheme().equals("content")) { + try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) { + if (cursor != null && cursor.moveToFirst()) { + int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + if (index != -1) { + result = cursor.getString(index); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + if (result == null) { + result = uri.getPath(); + int cut = result.lastIndexOf('/'); + if (cut != -1) { + result = result.substring(cut + 1); + } + } + return result; } } diff --git a/android/src/main/java/ee/forgr/capacitor/uploader/UploaderPlugin.java b/android/src/main/java/ee/forgr/capacitor/uploader/UploaderPlugin.java index 9e6a523..ccd7c16 100644 --- a/android/src/main/java/ee/forgr/capacitor/uploader/UploaderPlugin.java +++ b/android/src/main/java/ee/forgr/capacitor/uploader/UploaderPlugin.java @@ -1,25 +1,79 @@ package ee.forgr.capacitor.uploader; +import android.content.Context; import com.getcapacitor.JSObject; import com.getcapacitor.Plugin; import com.getcapacitor.PluginCall; import com.getcapacitor.PluginMethod; import com.getcapacitor.annotation.CapacitorPlugin; +import net.gotev.uploadservice.data.UploadInfo; +import net.gotev.uploadservice.network.ServerResponse; +import net.gotev.uploadservice.observer.request.RequestObserverDelegate; + +import java.util.Map; +import java.util.HashMap; +import java.util.Iterator; + @CapacitorPlugin(name = "Uploader") public class UploaderPlugin extends Plugin { - private Uploader implementation = new Uploader(); + private Uploader implementation; + + @Override + public void load() { + implementation = new Uploader(getContext(), new RequestObserverDelegate() { + @Override + public void onProgress(Context context, UploadInfo uploadInfo) { + JSObject progress = new JSObject(); + progress.put("id", uploadInfo.getUploadId()); + progress.put("progress", uploadInfo.getProgressPercent()); + notifyListeners("progress", progress); + } + + @Override + public void onSuccess(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) { + JSObject result = new JSObject(); + result.put("id", uploadInfo.getUploadId()); + result.put("responseCode", serverResponse.getCode()); + result.put("responseBody", serverResponse.getBodyString()); + notifyListeners("completed", result); + } + + + @Override + public void onError(Context context, UploadInfo uploadInfo, Throwable exception) { + JSObject error = new JSObject(); + error.put("id", uploadInfo.getUploadId()); + error.put("error", exception.getMessage()); + notifyListeners("error", error); + } + + @Override + public void onCompleted(Context context, UploadInfo uploadInfo) { + // This method is called after onSuccess or onError + } + + @Override + public void onCompletedWhileNotObserving() { + // This method is called when the upload completes while the observer is not registered + } + }); + } @PluginMethod public void startUpload(PluginCall call) { String filePath = call.getString("filePath"); String serverUrl = call.getString("serverUrl"); - Map headers = call.getMap("headers"); - String notificationTitle = call.getString("notificationTitle"); + JSObject headersObj = call.getObject("headers"); + Map headers = JSObjectToMap(headersObj); + String notificationTitle = call.getString("notificationTitle", "File Upload"); + try { - String id = implementation.startUpload(filePath, serverUrl, headers, notificationTitle,); - call.resolve(id); + String id = implementation.startUpload(filePath, serverUrl, headers, notificationTitle); + JSObject result = new JSObject(); + result.put("id", id); + call.resolve(result); } catch (Exception e) { call.reject(e.getMessage()); } @@ -35,4 +89,15 @@ public void removeUpload(PluginCall call) { call.reject(e.getMessage()); } } + + private Map JSObjectToMap(JSObject object) { + Map map = new HashMap<>(); + if (object != null) { + for (Iterator it = object.keys(); it.hasNext(); ) { + String key = it.next(); + map.put(key, object.getString(key)); + } + } + return map; + } }