Skip to content

Commit

Permalink
WIP Android
Browse files Browse the repository at this point in the history
  • Loading branch information
riderx committed Sep 3, 2024
1 parent 7c0fb72 commit c09344f
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 10 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ npm install @capgo/capacitor-uploader
npx cap sync
```

## Android:

Add the following to your `AndroidManifest.xml` file:

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
```

## API

<docgen-index>
Expand Down
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
33 changes: 32 additions & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ee.forgr.capacitor.uploader">

<application>
<service
android:name="net.gotev.uploadservice.UploadService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="dataSync">
<intent-filter>
<action android:name="android.intent.action.RUN" />
</intent-filter>
</service>

<service
android:name="net.gotev.uploadservice.UploadJobService"
android:enabled="true"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>

<receiver
android:name="net.gotev.uploadservice.UploadServiceBroadcastReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="net.gotev.uploadservice.broadcast.status" />
</intent-filter>
</receiver>
</application>

</manifest>
112 changes: 108 additions & 4 deletions android/src/main/java/ee/forgr/capacitor/uploader/Uploader.java
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String, String> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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<String, String> headers = call.getMap("headers");
String notificationTitle = call.getString("notificationTitle");
JSObject headersObj = call.getObject("headers");
Map<String, String> 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());
}
Expand All @@ -35,4 +89,15 @@ public void removeUpload(PluginCall call) {
call.reject(e.getMessage());
}
}

private Map<String, String> JSObjectToMap(JSObject object) {
Map<String, String> map = new HashMap<>();
if (object != null) {
for (Iterator<String> it = object.keys(); it.hasNext(); ) {
String key = it.next();
map.put(key, object.getString(key));
}
}
return map;
}
}

0 comments on commit c09344f

Please sign in to comment.